Need help speeding up my solution for Project Euler #650 - python

So Ive been working on project Euler problem #650, and it generates the correct answer for smaller values, but they are looking for the solution to values up to 20,000. At the speed my code is currently running that will take like a billion years. Any tips for improving this code and making it more efficient/faster would be appreciated. Here is a link to the problem: https://projecteuler.net/problem=650
and the code
from scipy.special import comb
import math
import time
t0 = time.time()
def B(x):
#takes product of binomial coefficients
product = 1
for i in range(x + 1):
product *= comb(x, i, exact=True)
return product
def D(y):
#Sums all divisors of the product of binom. coefficients
x = B(y)
summation = []
for i in range(1, int(math.sqrt(x)) + 1):
if x % i == 0:
summation.append(i)
summation.append(x // i)
summation = list(dict.fromkeys(summation))
return sum(summation)
def S(z):
"""sums up all the sums of divisors of the product of the binomial
coefficients"""
sigma = []
for i in range(1, z + 1):
sigma.append(D(i))
return sum(sigma)
print(S(20000))
t1 = time.time()
total = t1 - t0
print(total)

me too
but you can try this
from math import factorial
def n_choose_k(n, k):
if k == 0:
return 1
else:
return factorial(n)//((factorial(k))*factorial(n-k))
def B(n):
prods = []
for k in range(0, n):
prods.append(n_choose_k(n, k))

Related

Implementing the Karatsuba algorithm for multiplying polynomials

I found the Karatsuba algorithm (divide-and-conquer with reduced multiplications of about half width) for multiplying polynomials as below:
reference
and tried to implement it in Python. However, I do not understand the final recursive steps and U+[V+W]x**(n/2)+Zx**n at the end. So I am clueless as to how to correct my codes.
Update: I was able to successfully implement the below algorithm with the following code.
def PolynomialMult(n, p, q):
# Multiply two polynomials
def add(poly1, poly2):
result = [0] * max(len(poly1), len(poly2))
for i in range(len(result)):
if i < len(poly1):
result[i] += poly1[i]
if i < len(poly2):
result[i] += poly2[i]
return result
def increase_exponent(poly, n):
return [0] * n + poly
def multiply(p,q):
n = len(p)
m = n//2
if len(p)==1:
res = []
for i in range(n):
res.append(p[i]*q[i])
return res
else:
a0=p[0:m]
a1=p[m:n]
b0=q[0:m]
b1=q[m:n]
y=multiply(add(a0,a1),add(b0,b1))
u=multiply(a0,b0)
z=multiply(a1,b1)
diff = add(y,[(-1 * x) for x in add(u,z)])
add1 = add(u, increase_exponent(diff,m))
add2 = increase_exponent(z,n)
return add(add1, add2)
return multiply(p,q)
But I am not able to make it within the time limit for two test cases.
How I can improve this?

For a user input n, and 1<=i<j<=n, find the number of pairs where i*i*i=j*j using python

For a user input n, and 1<=i<j<=n, find the number of pairs where i*i*i=j*j using python
The program needs to take input from the user and if user input is 50, the output should be 3 as there are 3 such pairs :(1,1), (4,8), (9,27)using python.
def solution(n):
count=0
for i in range(1,n):
for j in range(i,n):
if (i**3==j*j):
count+=1
return count
n=int(input())
out=solution(n)
print(out)
This is the function I wrote. It works, but in the site I am practicing, it times out and asks me to optimize it further. What can I do?
You may not count how many time you find a match, but save the indices :
def solution(n):
result = []
for i in range(1, n):
for j in range(i, n):
if i ** 3 == j ** 2:
result.append((i, j))
return result
# with list comprehension
def solution(n):
return [(i, j) for i in range(1, n) for j in range(i, n) if i ** 3 == j ** 2]
OPTIMIZE
By looking at the values, you can determine which values can match, to get i**2 == j**3 t=you need i = x**3 and j = x**2 so one loop is sufficient :
def solution(n):
result = []
for i in range(1, ceil(n ** (1 / 3))):
result.append((i ** 2, i ** 3))
return result
# with list comprehension
def solution(n):
return [(i ** 2, i ** 3) for i in range(1, ceil(n ** (1 / 3)))]
Being a programmer should not prevent to keep one minute away from the computer and thinking about the mathematical problem. As an integer can be factorized as a product of prime factors at a power, i and j have to share the same prime factors, and the equality will be true for each of those prime factors. But for prime factors is is evident that you need to have a common number k with: k2 = i and k3 = j.
So the problem can be reduced to finding all numbers k, k >= 1 and k3 <= n. And the i,j pairs if you need them are just k2, k3
A trivial way is:
def solution(n)
count = 0
for i in range(n):
if i * i * i * i * i * i <= n:
count += 1
else:
break
return count
with one single loop.
But you can guess that the result will be close to n1/6, which will lead immediately to the result:
def solution(n):
def i6(i):
j = i *i * i
return j * j
i = int(n ** (1./6))
if (i == 0): return 1 # should never occur but floating point
# inaccuracy can give WEIRD results
if i6(i) > n:
return i - 1 # still floating point inaccuracy
if i6(i+1) <= n: # over convervative
return i + 1
return i
Only 3 tests whatever the value of n, at least up to 248 (mantissa size of a double value)

Memory limit exceeded in Python

I am solving a problem that needs either a list of integers or a dictionary of size 10^18. Upon running the code the compiler throws an error message saying "Memory Limit Exceeded".
Here is my code:
def fun(l, r, p):
#f = [None, 1, 1]
f = {0:0, 1:1, 2:1}
su = 0
for i in range(1, r):
if i%2 == 0:
f[i+2] = 2*f[i+1] - f[i] + 2
#f.append(2*f[i+1] - f[i] + 2)
else:
f[i+2] = 3*f[i]
#f.append(3*f[i])
for k in range(l, r):
su = su + f[k]
su = (su + f[r]) % p
print(su)
t, p = input().split()
p = int(p)
t = int(t)
#t = 3
#p = 100000007
for i in range(t):
l , r = input().split()
l = int(l)
r = int(r)
fun(l, r, p)
It is showing memory limit exceeded with a maximum memory usage of 306612 KiB.
Two observations here:
You don't need to store all numbers simultaneously. You can use the deque and generator functions to generate the numbers by keeping track of only the last three digits generated instead of the entire sequence.
import itertools
from collections import deque
def infinite_fun_generator():
seed = [0, 1, 1]
dq = deque(maxlen=2)
dq.extend(seed)
yield from seed
for i in itertools.count(1):
if i % 2 == 0:
dq.append(2 * dq[-1] - dq[-2] + 2)
else:
dq.append(3 * dq[-2])
yield dq[-1]
def fun(l, r, p):
funs = itertools.islice(infinite_fun_generator(), l, r + 1)
summed_funs = itertools.accumulate(funs, lambda a, b: (a + b) % p)
return deque(summed_funs, maxlen=1)[-1]
You might have a better chance asking this in Math.SE since I don't want to do the math right now, but just like with the Fibonacci sequence there's likely an analytic solution that you can use to compute the nth member of the sequence analytically without having to iteratively compute the intermediate numbers and it may even be possible to analytically derive a formula to compute the sums in constant time.

What do I get from Queue.get() (Python)

Overall question: How do I know what I am getting from a Queue object when I call Queue.get()? How do I sort it, or identify it? Can you get specific items from the Queue and leave others?
Context:
I wanted to learn a little about multi-proccessing (threading?) to make solving a matrix equation more efficient.
To illustrate, below is my working code for solving the matrix equation Ax = b without taking advantage of multiple cores. The solution is [1,1,1].
def jacobi(A, b, x_k):
N = len(x_k)
x_kp1 = np.copy(x_k)
E_rel = 1
iteration = 0
if (N != A.shape[0] or N != A.shape[1]):
raise ValueError('Matrix/vector dimensions do not match.')
while E_rel > ((10**(-14)) * (N**(1/2))):
for i in range(N):
sum = 0
for j in range(N):
if j != i:
sum = sum + A[i,j] * x_k[j]
x_kp1[i] =(1 / A[i,i]) * (b[i] - sum)
E_rel = 0
for n in range(N):
E_rel = E_rel + abs(x_kp1[n] - x_k[n]) / ((abs(x_kp1[n]) + abs(x_k[n])) / 2)
iteration += 1
# print("relative error for this iteration:", E_rel)
if iteration < 11:
print("iteration ", iteration, ":", x_kp1)
x_k = np.copy(x_kp1)
return x_kp1
if __name__ == '__main__':
A = np.matrix([[12.,7,3],[1,5,1],[2,7,-11]])
b = np.array([22.,7,-2])
x = np.array([1.,2,1])
print("Jacobi Method:")
x_1 = jacobi(A, b, x)
Ok, so I wanted to convert this code following this nice example: https://p16.praetorian.com/blog/multi-core-and-distributed-programming-in-python
So I got some code that runs and converges to the correct solution in the same number of iterations! That's really great, but what is the guarantee that this happens? It seems like Queue.get() just grabs whatever result from whatever process finished first (or last?). I was actually very surprised when my code ran, as I expected
for i in range(N):
x_update[i] = q.get(True)
to jumble up the elements of the vector.
Here is my code updated using the multi-processing library:
import numpy as np
import multiprocessing as mu
np.set_printoptions(precision=15)
def Jacobi_step(index, initial_vector, q):
N = len(initial_vector)
sum = 0
for j in range(N):
if j != i:
sum = sum + A[i, j] * initial_vector[j]
# this result is the updated element at given index of our solution vector.
q.put((1 / A[index, index]) * (b[index] - sum))
if __name__ == '__main__':
A = np.matrix([[12.,7,3],[1,5,1],[2,7,-11]])
b = np.array([22.,7,-2])
x = np.array([1.,2,1])
q = mu.Queue()
N = len(x)
x_update = np.copy(x)
p = []
error = 1
iteration = 0
while error > ((10**(-14)) * (N**(1/2))):
# assign a process to each element in the vector x,
# update one element with a single Jacobi step
for i in range(N):
process = mu.Process(target=Jacobi_step(i, x, q))
p.append(process)
process.start()
# fill in the updated vector with each new element aquired by the last step
for i in range(N):
x_update[i] = q.get(True)
# check for convergence
error = 0
for n in range(N):
error = error + abs(x_update[n] - x[n]) / ((abs(x_update[n]) + abs(x[n])) / 2)
p[i].join()
x = np.copy(x_update)
iteration += 1
print("iteration ", iteration, ":", x)
del p[:]
A Queue is first-in-first-out which means the first element inserted is the first element retrieved, in order of insertion.
Since you have no way to control that, I suggest you insert tuples in the Queue, containing the value and some identifying object that can be used to sort/relate to the original computation.
result = (1 / A[index, index]) * (b[index] - sum)
q.put((index, result))
This example puts the index in the Queue together with the result, so that when you .get() later you get the index too and use it to know which computation this is for:
i, x_i = q.get(True)
x_update[i] = x_i
Or something like that.

How to Replace Argument in List for Prime Generator

So I have spent tonight working on putting together this Prime generator based on the Sieve of Eratosthenes. Here is the code:
n = input("What number do you want to calculate to? ")
import time
start = time.time()
def pr(l):
ln = l+1
p = range(2, ln)
for k in p:
f = range(k, ln, k)
for f in f[1:]:
if f in p:
p.remove(f)
return p
print pr(n)
end = time.time() - start
print "This took: ",end
I think the main thing I would like to do is speed it up a bit. I am pretty sure that changing the p.remove(f) function to something like p[f] = 0 would speed it up, but that doesn't work. Does anyone know what I am doing wrong? Or is there an even faster was to do this?
Here's my version of the Sieve of Eratosthenes:
def primes(n): # sieve of eratosthenes
ps, sieve = [], [True] * (n + 1)
for p in range(2, n + 1):
if sieve[p]:
ps.append(p)
for i in range(p * p, n + 1, p):
sieve[i] = False
return ps
Please time it and let me know how it compares to your version.

Categories