How can I improve my code for euler 14? - python

I solved Euler problem 14 but the program I used is very slow. I had a look at what the others did and they all came up with elegant solutions. I tried to understand their code without much success.
Here is my code (the function to determine the length of the Collatz chain
def collatz(n):
a=1
while n!=1:
if n%2==0:
n=n/2
else:
n=3*n+1
a+=1
return a
Then I used brute force. It is slow and I know it is weak. Could someone tell me why my code is weak and how I can improve my code in plain English.
Bear in mind that I am a beginner, my programming skills are basic.

Rather than computing every possible chain from the start to the end, you can keep a cache of chain starts and their resulting length. For example, for the chain
13 40 20 10 5 16 8 4 2 1
you could remember the following:
The Collatz chain that starts with 13 has length 10
The Collatz chain that starts with 40 has length 9
The Collatz chain starting with 20 has length 8
... and so on.
We can then use this saved information to stop computing a chain as soon as we encounter a number which is already in our cache.
Implementation
Use dictionaries in Python to associate starting numbers with their chain length:
chain_sizes = {}
chain_sizes[13] = 10
chain_sizes[40] = 9
chain_sizes[40] # => 9
20 in chain_sizes # => False
Now you just have to adapt your algorithm to make use of this dictionary (filling it with values as well as looking up intermediate numbers).
By the way, this can be expressed very nicely using recursion. The chain sizes that can occur here will not overflow the stack :)

Briefly, because my English is horrible ;-)
Forall n >= 1, C(n) = n/2 if n even,
3*n + 1 if n odd
It is possible to calculate several consecutive iterates at once.
kth iterate of a number ending in k 0 bits:
C^k(a*2^k) = a
(2k)th iterate of a number ending in k 1 bits:
C^(2k)(a*2^k + 2^k - 1) = a*3^k + 3^k - 1 = (a + 1)*3^k - 1
Cf. formula on Wikipédia article (in French); see also my website (in French), and Module tnp1 in my Python package DSPython.
Combine the following code with the technique of memoization explained by Niklas B :
#!/usr/bin/env python
# -*- coding: latin-1 -*-
from __future__ import division # Python 3 style in Python 2
from __future__ import print_function # Python 3 style in Python 2
def C(n):
"""Pre: n: int >= 1
Result: int >= 1"""
return (n//2 if n%2 == 0
else n*3 + 1)
def Ck(n, k):
"""Pre: n: int >= 1
k: int >= 0
Result: int >= 1"""
while k > 0:
while (n%2 == 0) and k: # n even
n //= 2
k -= 1
if (n == 1) and k:
n = 4
k -= 1
else:
nb = 0
while (n > 1) and n%2 and (k > 1): # n odd != 1
n //= 2
nb += 1
k -= 2
if n%2 and (k == 1):
n = (n + 1)*(3**(nb + 1)) - 2
k -= 1
elif nb:
n = (n + 1)*(3**nb) - 1
return n
def C_length(n):
"""Pre: n: int >= 1
Result: int >= 1"""
l = 1
while n > 1:
while (n > 1) and (n%2 == 0): # n even
n //= 2
l += 1
nb = 0
while (n > 1) and n%2: # n odd != 1
n //= 2
nb += 1
l += 2
if nb:
n = (n + 1)*(3**nb) - 1
return l
if __name__ == '__main__':
for n in range(1, 51):
print(n, ': length =', C_length(n))

Related

Find count of possible power of the given element

You are given an array A having N integers. An element X in A is called perfect if X can be written as Y**Z for any Y >0 and Z>1
1<= N <= 10^5
1<= A[i] <= 10^5
Input:
2
9
6
Output
1
9 can be written as 3^2
def solve(N,A):
count=0
for i in A:
for j in range(1,int(i)//2):
for k in range(1,int(j):
if j**k == int(i):
count=count+1
i = i + 1
return count
This approach gives me correct answer for any type of input in my system unless it is in competitive coding IDE
The error message read Time Limit Exceeded
How do I overcome this problem ?
You can try simple preprocessing.
First of all, based on limits you need to check approximately n * 20 numbers (because 2 ** 20 > N), so it's O(n) - good, next when you processed all possible numbers you can simply compare your input with preprocessed data as follows:
def solve(n, a):
MAXN = 10 ** 5 + 1
is_perfect = [False] * MAXN
for number in range(1, MAXN):
for power in range(2, 20):
if number ** power > MAXN:
break
is_perfect[number**power] = True
counter = 0
for element in a:
if is_perfect[element]:
counter = counter + 1
return counter
Final complexity is O(n)

Use Recursion to Find Distinct Ways N can be computed

Was recently trying to solve this coding challenge from a company and I was stumped.
Let T(n) denote the number of different ways that a value of n cents, where n >= 4 and n is even, can be made by using 4-cent and 6-cent coins. For example, if n = 12 then we can use 3 4-cent coins or 2 6-cent coins, so T(12) = 2. Write a recursive algorithm in Python to find T(n) for n >= 4 and n is even.
I nailed down the base cases to be T(n < 4 or n not even) = 0, T(4) = 1 distinct way (1 4-cent coin) and T(6) = 1 distinct way (1 6-cent coin). But I'm not entirely sure how to proceed with a value greater than 6 and is even. Actually, if n > 4 and is even I did think of using modulo (%), so
if(n % 4 == 0): increment count
if(n % 6 == 0): increment count
I guess, I'm stuck on the recursive part because the two if-statements I've computed would count as only a single a way whereas there can be multiple ways to compute N.
Not recursive, but should help you get started.
To determine unique solutions, you are basically asking for partitions of N such that N1 + N2 = N and N1 % 4 == 0 and N2 % 6 == 0. An iterative solution would go something like this:
count = 0
for j in range(0, N + 1, 4):
if (N - j) % 6 == 0:
count += 1
Turning this loop into a recursion is trivial:
def count(N):
def count4(N, n4):
if n4 > N:
return 0
return int((N - n4) % 6 == 0) + count4(N, n4 + 4)
if N < 4 or N % 2:
return 0
return count4(N, 0)
Assuming that ways means "exact order in which coins are laid out", here is a recursive solution.
def T_recurse (n):
if 0 == n:
return 1
elif n < 3:
return 0
else:
return T_recurse(n - 4) + T_recurse(n - 6)
print(T_recurse(100))
And a faster solution
def T_iter(n):
if n < 0:
return 0
else:
answers = [1, 0, 0, 0, 1, 0]
while len(answers) <= n:
answers.append(answers[-4] + answers[-6])
return answers[n]
print(T_iter(100))
(There is also an analytical solution involving the roots of the polynomial x^6 - x^2 - 1, but that is slower to calculate in practice.)
Assuming that ways means "this many of one, that many of the other", then here is a recursive solution:
def S_recurse (n, coins):
if 0 == n:
return 1
elif n < 0:
return 0
elif len(coins) == 0:
return 0
else:
return S_recurse(n - coins[0], coins) + S_recurse(n, coins[1:])
S_recurse(12, [4, 6])
The recursive slowdown is not as bad though still exponential. However but iterative gives you quadratic:
def S_iter (n, coins):
last_row = [0 for i in range(n + 1)]
last_row[0] = 1
for coin in coins:
this_row = []
for i in range(n+1):
if i < coin:
this_row.append(last_row[i])
else:
this_row.append(last_row[i] + this_row[i - coin])
last_row = this_row
return last_row[n]
You can use an optional parameter to keep track of the current sum of 6-cent coins for a given recursive call, and return 1 when the given number is divisible by 4 after deducting the sum of 6's:
def count46(n, sum6=0):
return sum6 <= n and (((n - sum6) % 4) == 0) + count46(n, sum6 + 6)
so that:
for i in range(4, 24, 2):
print(i, count_4_6(i))
outputs:
4 1
6 1
8 1
10 1
12 2
14 1
16 2
18 2
20 2
22 2
Not the most optimized but it returns an array of all distinct solutions
def coins(n, sum=0, current=[], answers=[]):
if sum > n:
return
if sum == n:
answers.append(current)
return
a4 = list(current)
a4.append(4)
coins(n, sum+4, a4, answers)
lastIndex = len(current) - 1
if len(current) == 0 or current[lastIndex] == 6:
a6 = list(current)
a6.append(6)
coins(n, sum+6, a6, answers)
return answers
Try it online!

How to get a prime collection within 2^63 - 1 in Python

I want to get a prime number set within 2^63 - 1 in Python,I have seen the following code on the web:
limit = 2**63 - 2
p = [True] * (limit + 1)
# p = bitarray(limit + 1)
p[0] = p[1] = False
for i in range(2, int(math.sqrt(limit) + 1)):
if p[i]:
for j in range(i * i, limit + 1, i):
p[j] = False
prime = [i for i in range(limit + 1) if p[i]]
print(prime)
But when I run this program, the compiler complains that can not fit 'int' into an index-sized integer.
I tried to solve the problem with bitarray, but the index of the array is still too big.
You can use the following code. It is using the Sieve of Eratosthenes in combination with a generator function in order to reduce the memory usage of this algorithm. It is furthermore exploiting the less commonly known fact that every prim number > 4 can be written as 6*n ± 1.
import math
limit = 2 ** 63 - 1
def isPrim(n, belowPrims):
limit = int(math.sqrt(n))
for prim in belowPrims:
if prim > limit: break
if n % prim == 0: return False
return True
def calcPrims():
yield 2
yield 3
toTest, nextN = [], 6
while True:
p1 = nextN - 1
if isPrim(p1, toTest):
yield p1
toTest.append(p1)
p2 = nextN + 1
if isPrim(p2, toTest):
yield p2
toTest.append(p2)
nextN += 6
for prim in calcPrims():
if prim > limit:
break
print(prim)
Edit
This link here https://primes.utm.edu/notes/faq/six.html explains briefly why every prim number can be written in the form 6*n ± 1.
You can use sympy:
import sympy
print(list(sympy.primerange(0,2**63-1)))
but as 2^63 is quite large this will take some time.
if you have a primes() generator of some kind, you could do this:
is_prime_var = 0
MAX = 1 << 5
last_p = 0
for p in primes():
if p > MAX:
break
print(p, p-last_p)
is_prime_var <<= (p - last_p)
is_prime_var |= 1
last_p = p
is_prime_var <<= (MAX - last_p - 1)
now the locations of the primes are stored (in reversed order) in the integer is_prime_var.
then the expression (is_prime >> (MAX-n-1)) & 1 would be 1 if n is prime; 0 otherwise:
def is_prime(n):
return bool((is_prime_var >> (MAX-n-1)) & 1)
you could use primes() from this excellent answer as prime generator.
thers is also this answer of mine about a fast and memory-efficient sieve of eratosthenes. might also be interesting.

Dynamic programming for primitive calculator

I'm dealing with the problem, that is pretty similar to change coins problem.
I need to implement a simple calculator, that can perform the following three operations with the current number x: multiply x by 2, multiply x by 3, or add 1 to x.
Goal is given a positive integer n, find the minimum number of operations needed to obtain the number n starting from the number 1.
I made a greedy approach to that, bur it shows incorrect results
import sys
def optimal_sequence(n):
sequence = []
while n >= 1:
sequence.append(n)
if n % 3 == 0:
n = n // 3
elif n % 2 == 0:
n = n // 2
else:
n = n - 1
return reversed(sequence)
input = sys.stdin.read()
n = int(input)
sequence = list(optimal_sequence(n))
print(len(sequence) - 1)
for x in sequence:
print(x)
For example:
Input: 10
Output:
4
1 2 4 5 10
4 steps. But the correct one is 3 steps:
Output:
3
1 3 9 10
I read about dynamic programming, and hope I could implement it here. But, I can't get how to use it properly in particular case, can someone give me an advice?
Just solve it with a simple recursion and Memoization:
Code:
d = {}
def f(n):
if n == 1:
return 1, -1
if d.get(n) is not None:
return d[n]
ans = (f(n - 1)[0] + 1, n - 1)
if n % 2 == 0:
ret = f(n // 2)
if ans[0] > ret[0]:
ans = (ret[0] + 1, n // 2)
if n % 3 == 0:
ret = f(n // 3)
if ans[0] > ret[0]:
ans = (ret[0] + 1, n // 3)
d[n] = ans
return ans
def print_solution(n):
if f(n)[1] != -1:
print_solution(f(n)[1])
print n,
def solve(n):
print f(n)[0]
print_solution(n)
print ''
solve(10)
Hint: f(x) returns a tuple (a, b), which a denotes the minimum steps to get x from 1, and b denotes the previous number to get the optimum solution. b is only used for print the solution.
Output:
4 # solution for 10
1 3 9 10
7 # solution for 111
1 2 4 12 36 37 111
You may debug my code and to learn how it works. If you are beginner at DP, you could read my another SO post about DP to get a quick start.
Since Python can't recurse a lot (about 10000), I write an iterative version:
# only modified function print_solution(n) and solve(n)
def print_solution(n):
ans = []
while f(n)[1] != -1:
ans.append(n)
n = f(n)[1]
ans.append(1)
ans.reverse()
for x in ans:
print x,
def solve(n):
for i in range(1, n):
f(i)[0]
print_solution(n)
print ''
solve(96234) # 1 3 9 10 11 22 66 198 594 1782 5346 16038 16039 32078 96234

Probability of finding a prime (using miller-rabin test)

I've implemented Miller-Rabin primality test and every function seems to be working properly in isolation. However, when I try to find a prime by generating random numbers of 70 bits my program generates in average more than 100000 numbers before finding a number that passes the Miller-Rabin test (10 steps). This is very strange, the probability of being prime for a random odd number of less than 70 bits should be very high (more than 1/50 according to Hadamard-de la Vallée Poussin Theorem). What could be wrong with my code? Would it be possible that the random number generator throws prime numbers with very low probability? I guess not... Any help is very welcome.
import random
def miller_rabin_rounds(n, t):
'''Runs miller-rabin primallity test t times for n'''
# First find the values r and s such that 2^s * r = n - 1
r = (n - 1) / 2
s = 1
while r % 2 == 0:
s += 1
r /= 2
# Run the test t times
for i in range(t):
a = random.randint(2, n - 1)
y = power_remainder(a, r, n)
if y != 1 and y != n - 1:
# check there is no j for which (a^r)^(2^j) = -1 (mod n)
j = 0
while j < s - 1 and y != n - 1:
y = (y * y) % n
if y == 1:
return False
j += 1
if y != n - 1:
return False
return True
def power_remainder(a, k, n):
'''Computes (a^k) mod n efficiently by decomposing k into binary'''
r = 1
while k > 0:
if k % 2 != 0:
r = (r * a) % n
a = (a * a) % n
k //= 2
return r
def random_odd(n):
'''Generates a random odd number of max n bits'''
a = random.getrandbits(n)
if a % 2 == 0:
a -= 1
return a
if __name__ == '__main__':
t = 10 # Number of Miller-Rabin tests per number
bits = 70 # Number of bits of the random number
a = random_odd(bits)
count = 0
while not miller_rabin_rounds(a, t):
count += 1
if count % 10000 == 0:
print(count)
a = random_odd(bits)
print(a)
The reason this works in python 2 and not python 3 is that the two handle integer division differently. In python 2, 3/2 = 1, whereas in python 3, 3/2=1.5.
It looks like you should be forcing integer division in python 3 (rather than float division). If you change the code to force integer division (//) as such:
# First find the values r and s such that 2^s * r = n - 1
r = (n - 1) // 2
s = 1
while r % 2 == 0:
s += 1
r //= 2
You should see the correct behaviour regardless of what python version you use.

Categories