Project Euler #101 - how to work around numpy polynomial overflow? - python

Project Euler #101
I just started learning Numpy and it so far looks pretty straightforward to me.
One thing I ran into is that when I evaluate the polynomial, the result is a int32, so an overflow would occur.
u = numpy.poly1d([1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1])
for i in xrange(1, 11):
print(i, u(i))
The results are:
(1, 1)
(2, 683)
(3, 44287)
(4, 838861)
(5, 8138021)
(6, 51828151)
(7, 247165843)
(8, 954437177)
(9, -1156861335)
(10, 500974499)
The last two items are clearly incorrect.
The work around I can think of is factoring the coefficients by 100
u = numpy.poly1d([0.01, -0.01, 0.01, -0.01, 0.01, -0.01, 0.01, -0.01, 0.01, -0.01, 0.01])
for i in xrange(1, 11):
print(i, int(u(i) * 100))
This time the results are correct
(1, 1)
(2, 682)
(3, 44286)
(4, 838860)
(5, 8138020)
(6, 51828151)
(7, 247165843)
(8, 954437177)
(9, 3138105961L)
(10, 9090909091L)
Is there a better way? Does Numpy allow me to change the data type? Thanks.

It wasn't the scaling by 100 that helped, but the fact that the numbers given were floats instead of ints, and thus had a higher range. Due to the floating-point calculations, there are some inaccuracies introduced to the calculations as you have seen.
You can specify the type manually like this:
u = numpy.poly1d(numpy.array([1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1], dtype=numpy.int64))
The calculations for this problem fit in 64-bit ints, so this will work.
The supported types are listed here.

Interjay posted a better answer while I was writing this up, but I figured you might want an alternative anyway.
Here's a simple implementation for the examples you showed:
class Poly(object):
def __init__(self, coefficients):
assert len(coefficients) > 0
self.coefficients = coefficients
def __call__(self, value):
total = self.coefficients[0]
for c in self.coefficients[1:]:
total = total * value + c
return total
along with some tests
assert Poly([5])(1) == 5
assert Poly([7])(1) == 7
assert Poly([2,3])(5) == 13
assert Poly([1,0,0,0,0])(-2) == 16
u = Poly([1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1])
for i in range(1, 11):
print (i, u(i))
and the rather useless
assert Poly([2,"!"])("Hello ") == "Hello Hello !"

Related

Generate grid of coordinate tuples

Assume a d-dimensional integer grid, containing n^d (n >= 1) points.
I am trying to write a function that takes the number of domain points n and the number of dimensions d and returns a set that contains all the coordinate points in the grid, as tuples.
Example: intGrid (n=2, dim=2) should return the set:
{(0,0), (0,1), (1,0), (1,1)}
Note: I cannot use numpy or any external imports.
Python has a good set of built-in modules that provides most of the basic functionality you will probably need to start getting your things done.
One of such good modules is itertools, where you will find all sorts of functions related to iterations and combinatorics. The perfect function for you is product, that you can use as below:
from itertools import product
def grid(n, dim):
return set(product(range(n), repeat=dim))
print(grid(2, 2))
# {(0, 0), (0, 1), (1, 0), (1, 1)}
print(grid(2, 3))
# {(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)}

Move a shape inside a list of lists

I have the following list of lists representing a matrix:
space = [
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
]
The number 1s represent an upside down L (like a gamma, "Γ"). How can I make this "object" move to the right, left, up and down as if it was a block? I move it with "asdw" keys.
Important: I am not able to use numpy, so thats makes the work way more difficult.
This is my failed method to make a RIGHT direction movement (btw it doesnt make the movement correctly), but i dont think its the best way and dont really think I can escalate it to other movements:
def show_space(space):
for line in space:
print(line)
x = input("Movement: ")
if x == 'd':
for i in range(4):
for j in range(4):
if space[i][j] == 1:
try:
if space[i][j+1] == 0:
space[i][j] = 0
space[i][j+1] = 1
if space[i][j+1] == 1 and space[i][j+2] == 0:
space[i][j] = 0
space[i][j+2] = 1
except IndexError:
pass
show_space(space)
Is there any other method I could try? Or correct the one Im using? Thanks in advance
EDIT:
The gamma not only should be able to move right up down left, it should also be able to move 90 degrees, mirror itself, and all possible shapes that form can take. So if i had to hardcode all possible gamma or L combinations, i would have to hardcode 48 possibilities. I dont know wether hardcoding that is the optimal way to be honest.
Im not saying hardcoding the postions is not acceptable, it could definitely be a solution, but I just dont feel like its the correct way. i may be wrong of course.
What do you think?
Here's how I'd suggest doing something like this:
Calculate all the indices for each gamma shape in the matrix, kept stored in a dictionary with the each corner index tuple as the dictionary keys, then whenever the corner moves, figure out the indices that should be 1s, and assign to a copy of a matrix of zeros.
positions = {(0, 0): ((0, 1), (1, 0), (2, 0)),
(0, 1): ((0, 2), (1, 1), (2, 1)),
(0, 2): ((0, 3), (1, 2), (2, 2)),
(1, 0): ((1, 1), (2, 0), (3, 0)),
(1, 1): ((1, 2), (2, 1), (3, 1)),
(1, 2): ((1, 3), (2, 2), (3, 2))}
def move_gamma(corner):
board = [[0 for _ in range(4)] for _ in range(4)]
try:
for (i, j) in (corner, *positions[corner]):
board[i][j] = 1
return board
except KeyError:
print("You can't move there!")
return board
def flip_h(board):
return [[*reversed(row)] for row in board]
def flip_v(board):
return [*reversed(board)]
def transpose(board):
return [[*t] for t in zip(*board)]
Demo:
In [3]: board = move_gamma((1, 1))
In [4]: print(*board, sep="\n")
[0, 0, 0, 0]
[0, 1, 1, 0]
[0, 1, 0, 0]
[0, 1, 0, 0]
In [5]: board = move_gamma((1, 2))
In [6]: print(*board, sep="\n")
[0, 0, 0, 0]
[0, 0, 1, 1]
[0, 0, 1, 0]
[0, 0, 1, 0]
In [7]: print(*transpose(board), sep="\n")
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 0, 0]
Just a heads up, you'd still need to implement the logic for mapping WASD to movement relative to the current corner indices.
If you need to stay within the confines of the standards library, you can use collection.deque, which has a rotate method that does exactly what you need and will have to implement in lists if you can't use deque.
This is what I can offer with deque.
NB. this wraps around the edges which might not be intended,
from collections import deque
space = deque([
deque([0, 1, 1, 0]),
deque([0, 0, 1, 0]),
deque([0, 0, 1, 0]),
deque([0, 0, 0, 0]),
])
def move(mvt):
if mvt == "s":
space.rotate(1)
elif mvt == "w":
space.rotate(-1)
elif mvt == "d":
[x.rotate(1) for x in space]
elif mvt == "a":
[x.rotate(-1) for x in space]
else:
raise NotImplementedError
move("d") # etc
Your answer is in your question - instead of keeping track of all space (array with mostly 0s), use an object (python dict) to instead specify the shape, and where to start drawing it e.g.:
{
'abs_pos': (0,1),
'rel_points': [(0,0), (0,1), (1,0), (2,0)]
}
Then moving it only amounts to updating the location of (e.g.) the upper-left corner abs_pos.
When it's time to print your map of all space, start from abs_pos and then add a dot at each point in rel_points.

How can I add a random binary info into current 'coordinate'? (Python)

This is part of the code I'm working on: (Using Python)
import random
pairs = [
(0, 1),
(1, 2),
(2, 3),
(3, 0), # I want to treat 0,1,2,3 as some 'coordinate' (or positional infomation)
]
alphas = [(random.choice([1, -1]) * random.uniform(5, 15), pairs[n]) for n in range(4)]
alphas.sort(reverse=True, key=lambda n: abs(n[0]))
A sample output looks like this:
[(13.747649802587832, (2, 3)),
(13.668274782626717, (1, 2)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (3, 0))]
Now I'm wondering is there a way I can give each element in 0,1,2,3 a random binary number, so if [0,1,2,3] = [0,1,1,0], (By that I mean if the 'coordinates' on the left list have the corresponding random binary information on the right list. In this case, coordinate 0 has the random binary number '0' and etc.) then the desired output using the information above looks like:
[(13.747649802587832, (1, 0)),
(13.668274782626717, (1, 1)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (0, 0))]
Thanks!!
One way using dict:
d = dict(zip([0,1,2,3], [0,1,1,0]))
[(i, tuple(d[j] for j in c)) for i, c in alphas]
Output:
[(13.747649802587832, (1, 0)),
(13.668274782626717, (1, 1)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (0, 0))]
You can create a function to convert your number to the random binary assigned. Using a dictionary within this function would make sense. Something like this should work where output1 is that first sample output you provide and binary_code would be [0, 1, 1, 0] in your example:
def convert2bin(original, binary_code):
binary_dict = {n: x for n, x in enumerate(binary_code)}
return tuple([binary_code[x] for x in original])
binary_code = np.random.randint(2, size=4)
[convert2bin(x[1], binary_code) for x in output1]

How to use two conditions in an if statement

I have the following equation:
result=[(i,j,k) for i in S for j in S for k in S if sum([i,j,k])==0]
I want to add another condition in the if statement such that my result set does not contain (0,0,0). I tried to do the following:
result=[(i,j,k) for i in S for j in S for k in S if sum([i,j,k])==0 && (i,j,k)!=(0,0,0)] but I am getting a syntax error pointing to the &&. I tested my expression for the first condition and it is correct.
You are looking for the and boolean operator instead:
result=[(i,j,k) for i in S for j in S for k in S if sum([i,j,k])==0 and (i,j,k)!=(0,0,0)]
&& is JavaScript, Java, Perl, PHP, Ruby, Go, OCaml, Haskell, MATLAB, R, Lasso, ColdFusion, C, C#, or C++ boolean syntax instead.
Apart from that error instead of triple nested for-loops you can also use itertools.product here to get the Cartesian product of S * S * S:
from itertools import product
result=[ x for x in product(S, repeat = 3) if sum(x)==0 and x != (0,0,0)]
Demo:
>>> S = [1, -1, 0, 0]
>>> [ x for x in product(S, repeat = 3) if sum(x) == 0 and x != (0,0,0)]
[(1, -1, 0), (1, -1, 0), (1, 0, -1), (1, 0, -1), (-1, 1, 0), (-1, 1, 0), (-1, 0, 1), (-1, 0, 1), (0, 1, -1), (0, -1, 1), (0, 1, -1), (0, -1, 1)]
result = [(i, j, k) for i in S
for j in S
for k in S
if sum([i, j, k]) == 0 and (i, j, k) != (0, 0, 0)]

python comprehension with multiple 'for' clauses and single 'if'

Imagine a discrete x,y,z space : I am trying to create an iterator which will return all points which lie within a sphere of some radial distance from a point.
My approach was to first look at all points within a larger cube which is guaranteed to contain all the points needed and then cull or skip points which are too far away.
My first attempt was:
x,y,z=(0,0,1)
dist=2
#this doesn't work
it_0=((x+xp,y+yp,z+zp) for xp in range(-dist,dist+1) for yp in range(-dist,dist+1) for zp in range(-dist,dist+1) if ( ((x-xp)**2+(y-yp)**2+(z-zp)**2) <= dist**2+sys.float_info.epsilon ) )
a simple
for d,e,f in it_0:
#print(d,e,f)
print( ((x-d)**2+(y-e)**2+(z-f)**2) <= dist**2+sys.float_info.epsilon, d,e,f)
verifies that it_0 does not produce correct results. I believe it is applying the conditional only to the third (ie: z) 'for' clause
The following works:
it_1=((x+xp,y+yp,z+zp) for xp in range(-dist,dist+1) for yp in range(-dist,dist+1) for zp in range(-dist,dist+1))
it_2=filter( lambda p: ((x-p[0])**2+(y-p[1])**2+(z-p[2])**2) <= dist**2+sys.float_info.epsilon, it_1)
It collects all the points, then filter those which don't fit the conditional.
I was hoping there might be a way to correct the first attempted implementation, or make these expressions more readable or compact.
First of all, I suggest you replace the triply-nested for loop with itertools.product(), like so:
import itertools as it
it_1 = it.product(range(-dist, dist+1), repeat=3)
If you are using Python 2.x, you should use xrange() here instead of range().
Next, instead of using filter() you could just use a generator expression:
it_2=(x, y, z for x, y, z in it_1 if ((x-p[0])**2+(y-p[1])**2+(z-p[2])**2) <= dist**2+sys.float_info.epsilon)
This would avoid some overhead in Python 2.x (since filter() builds a list), but for Python 3.x would be about the same; and even in Python 2.x you could use itertools.ifilter().
But for readability, I would package the whole thing up into a generator, like so:
import itertools as it
import sys
def sphere_points(radius=0, origin=(0,0,0), epsilon=sys.float_info.epsilon):
x0, y0, z0 = origin
limit = radius**2 + epsilon
for x, y, z in it.product(range(-radius, radius+1), repeat=3):
if (x**2 + y**2 + z**2) <= limit:
yield (x+x0, y+y0, z+z0)
I just changed the code from your original code. Each range for x, y, and z is adjusted to center around the origin point. When I test this code with a radius of 0, I correctly get back a single point, the origin point.
Note that I provided arguments to the function letting you specify radius, origin point, and even the value to use for epsilon, with defaults for each. I also unpacked the origin point tuple into explicit variables; I'm not sure if Python would optimize away the indexing operation or not, but this way we know there won't be any indexing going on inside the loop. (I think the Python compiler would probably hoist the limit calculation out of the loop, but I actually prefer it on its own line as shown here, for readability.)
I think the above is about as fast as you can write it in native Python, and I think it is a big improvement in readability.
P.S. This code would probably run a lot faster if it was redone using Cython.
http://cython.org/
EDIT: Code simplified as suggested by #eryksun in comments.
You've confused the meanings of xp, yp, zp in your generator expression:
it_0=((x+xp,y+yp,z+zp) for xp in range(-dist,dist+1) for yp in range(-dist,dist+1) for zp in range(-dist,dist+1) if ( ((x-xp)**2+(y-yp)**2+(z-zp)**2) <= dist**2+sys.float_info.epsilon ) )
xp, yp, and zp are already the distances from the center of the sphere along the various axes. Thus you should not be taking the difference from x, y, z again. The expression should be:
it_0=((x+xp,y+yp,z+zp) for xp in range(-dist,dist+1) for yp in range(-dist,dist+1) for zp in range(-dist,dist+1) if ( (xp**2+yp**2+zp**2) <= dist**2+sys.float_info.epsilon ) )
Your use of epsilon is a bit off. The documentation for sys.float_info describes it as the difference between 1 and the next representable float, so it is too small to make a difference on 2, let alone 2**2.
Secondly, all your points are measured from x,y,z in the filter/if then offset by it in the result expression (x+xp,y+yp,z+zp). You'll get a rather off-centre sphere that way, so try (xp,yp,zp).
Others have pointed out the logic errors. I'll address readability. Also note with the data given, there is no floating point involved, so an epsilon is not needed.
from itertools import product
from pprint import pprint
x,y,z = 0,0,1
r = 2
def points_in_circle(radius):
'''return a generator of all integral points in circle of given radius.'''
return ((x,y,z)
for x,y,z in product(range(-dist,dist+1),repeat=3)
if x**2 + y**2 + z**2 <= radius**2)
# List integral points of radius r around point (x,y,z).
pprint([(x+xp,y+yp,z+zp) for xp,yp,zp in points_in_circle(r)])
Output
[(-2, 0, 1),
(-1, -1, 0),
(-1, -1, 1),
(-1, -1, 2),
(-1, 0, 0),
(-1, 0, 1),
(-1, 0, 2),
(-1, 1, 0),
(-1, 1, 1),
(-1, 1, 2),
(0, -2, 1),
(0, -1, 0),
(0, -1, 1),
(0, -1, 2),
(0, 0, -1),
(0, 0, 0),
(0, 0, 1),
(0, 0, 2),
(0, 0, 3),
(0, 1, 0),
(0, 1, 1),
(0, 1, 2),
(0, 2, 1),
(1, -1, 0),
(1, -1, 1),
(1, -1, 2),
(1, 0, 0),
(1, 0, 1),
(1, 0, 2),
(1, 1, 0),
(1, 1, 1),
(1, 1, 2),
(2, 0, 1)]

Categories