Suppose we have the following recursion formula
I wrote the following code based on that recursion,
def f(k):
if(k <= 2):
return k
elif(k % 2 ==0):
return 5*f(k/2) + 1
else:
return f((k-1)/2) + 2
Is there a way to make this implementation faster and more efficient?
something like this
storage = dict()
def f(k):
if(k <= 2):
return k
elif k in storage:
return storage[k]
elif(k % 2 ==0):
storage[k] = 5*f(k/2) + 1
return storage[k]
else:
storage[k] = f((k-1)/2) + 2
return storage[k]
You should use integer divisions because results will always be integers. Also, you don't need the parentheses around conditions.
There is a decorator in the functools module to automatically cache (memoize) your function:
from functools import lru_cache
#lru_cache()
def f(k):
if k <= 2:
return k
elif k % 2 == 0:
return 5 * f(k//2) + 1
else:
return f((k-1)//2) + 2
Note that this function converges extremely rapidly (exponentially?). The cache may not improve it much.
Related
I am trying to perform a Collatz algorithm on the following code. It works fine when I use a range of 1-10 etc... However, if the range is for example 1-500,000 it's too slow and won't ever show me the output of the longest sequence.
numberArray = []
s=int(1)
f=int(10)
def collatz(n):
global count
if n == 1:
count += 1
numberArray.append(count)
return True
elif (n % 2) == 0:
count += 1
collatz(n/2)
else:
count += 1
collatz(3*n+1)
for i in range (s, f+1):
count = 0
ourNumber = i
collatz(i)
print(max(numberArray))
Stef means something like this, which uses a dictionary to memorise the values that have already been counted:
s = 1
f = 10000000
def collatz(n):
if n in collatz.memory:
return collatz.memory[n]
if (n % 2) == 0:
count = collatz(n//2)+1
else:
count = collatz((3*n+1)//2)+2
collatz.memory[n] = count
return count
collatz.memory = {1:0}
highest = max(collatz(i) for i in range(s, f+1))
highest_n = max(collatz.memory, key=collatz.memory.get)
print(f"collatz({highest_n}) is {highest}")
Output:
collatz(8400511) is 685
Use lru_cache decorator. Its function to memorize (cache) the returned value of function called with specific argument.
Also read how to write clean code in python
The next code solves your problem
from functools import lru_cache
number_array = []
s = 1
f = 500000
#lru_cache
def collatz(n: int):
if n == 1:
return 1
elif n % 2 == 0:
return 1 + collatz(n // 2)
else:
return 1 + collatz(3 * n + 1)
for i in range(s, f + 1):
number_array.append(collatz(i))
print(number_array)
I'm building a tool to output how many time the code run in python. For instance, when the input is
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
The output would be
def fib(n): 21891
if n <= 1: 10946
return n 10946
return fib(n-1) + fib(n-2) 8000
fib(20) 1
The number on the right shows how many time the code run.
What I doing is rewriting the function to
def fib(n):
dic["count_0"] += 1
if n <= 1:
dic["count_6"] += 1
return n
dic["count_7"] += 1
return fib(n-1) + fib(n-2)
dic["count_8"] += 1
fib(20)
dic["count_10"] += 1
Then handle the edge case like comments or return keyword. I wonder any other way to do this without to many hacks like this.
I wrote this rather poor Python function for prime factorization:
import math
def factor(n):
for i in range(2, int(math.sqrt(n)+1)):
if not n % i:
return [i] + factor(n//i)
return [n]
and it worked as expected, now I was interested in whether the performance could be better when using an iterative approach:
def factor_it(n):
r = []
i = 2
while i < int(math.sqrt(n)+1):
while not n % i:
r.append(i)
n //= i
i +=1
if n > 1:
r.append(n)
return r
But what I observed (while the functions gave the same results) was that the iterative function took longer to run. At least I had the feeling, when doing this:
number = 31123478114123
print(factor(number))
print(factor_it(number))
so I measured:
setup = '''
import math
def factor(n):
for i in range(2, int(math.sqrt(n)+1)):
if not n % i:
return [i] + factor(n//i)
return [n]
def factor_it(n):
r = []
i = 2
while i < int(math.sqrt(n)+1):
while not n % i:
r.append(i)
n //= i
i +=1
if n > 1:
r.append(n)
return r
'''
import timeit
exec(setup)
number = 66666667*952381*290201
print(factor(number))
print(factor_it(number))
print(timeit.Timer('factor('+str(number)+')',setup=setup).repeat(1,1))
print(timeit.Timer('factor_it('+str(number)+')',setup=setup).repeat(1,1))
And this is what I got:
[290201, 952381, 66666667]
[290201, 952381, 66666667]
[0.19888348945642065]
[0.7451271022307537]
Why is the recursive approach in this case faster than the iterative one?
I use WinPython-64bit-3.4.4.2 (Python 3.4.4 64bits).
It’s because you’re recomputing sqrt each time. This modification runs as fast as your recursive version:
def factor_it2(n):
r = []
i = 2
lim = int(math.sqrt(n)+1)
while i < lim:
while not n % i:
r.append(i)
n //= i
lim = int(math.sqrt(n)+1)
i += 1
if n > 1:
r.append(n)
return r
timeit gives me these times:
factor 0.13133816363922143
factor_it 0.5705408816539869
factor_it2 0.14267319543853973
I think the tiny difference that remains is due to for … in range(…) being faster than the equivalent while loop, as the for loop can use a generator instead of having to execute a bunch of comparisons.
I am writing a program that calculates the Pascal Identity of two variables, hard coded into the program, as I am new into Python and trying out caching and memoization. Here is what I have so far:
counter = 0
call_cacheK = {}
def callTest(n, k):
global counter
if n in call_cacheK:
return call_cacheK[n]
if k == 0:
return 1
elif k == n:
return 1
elif (1 <= k) and (k <= (n-1)):
counter += 1
#call_cacheK[n] = result
result = ((callTest(n-1, k) + callTest(n-1, k-1)))
print(result)
return result
callTest(20, 11)
#167,960
My function will output the final real answer with what it has now, but with a lot of outputted answers. I cannot seem to get how to properly store the values to be used in the cache.
How do I properly use call_cacheK to store the result values I have already used?
Thank you.
Lets see. First, you have a function of two variables, but store result in cache only by one parameter. So callTest(20, 11), callTest(20, 10), callTest(20, 9) will have one result in your cache. Lets rewrite your function a little:
call_cacheK = {}
def callTest(n, k):
if (n, k) in call_cacheK:
return call_cacheK[(n, k)]
if k == 0:
return 1
elif k == n:
return 1
elif (1 <= k) and (k <= (n-1)):
result = ((callTest(n-1, k) + callTest(n-1, k-1)))
call_cacheK[(n, k)] = result
print(result)
return result
Yes there is no counter variable because I didn't realize why do you need it :)
Also, as far as I can judge by the use of print(result), you probably use the Python3.x. If so, you can use standard cache implementing:
from functools import lru_cache
#lru_cache(maxsize=None)
def callTest2(n, k):
if k == 0:
return 1
elif k == n:
return 1
elif (1 <= k) and (k <= (n-1)):
result = ((callTest2(n-1, k) + callTest2(n-1, k-1)))
print(result)
return result
Good luck! :)
EDIT: I know I can import factorials but I'm doing this as an exercise
Trying to get the factor of a given number with a function in Python.
For example:
factorial(4) = 4 * 3 * 2 * 1 = 24
def factorial(x):
n = x
while n >= 0:
x = n * (n - 1)
n -= 1
return x
try like this: to make your code work
def factorial(x):
n = 1 # this will store the factorial value
while x > 0:
n = n*x
x -= 1
return n
you got many advice on comments follow it
A good way of approaching this would be using recursion where a function calls itself. See Function for Factorial in Python
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
But in your case your return statement actually breaks out of the while loop. So if you pass in 5 you get 20 back which is obviously not 5! (5 factorial).
Instead, try this:
def factorial(x):
n = 1
while x > 1:
n *= x
x -= 1
return n
print (factorial(5))
But do have a go at the recursive approach.
If you really want to cheat try:
import math
math.factorial(n)
I present an even shorter code for recursive factorial calculation. Not sure if it is faster or slower than other approaches:
def fac(n):
return 1 if n==1 else n*fac(n-1)
fac(10)
3628800
def factorial(n):
total = 1
for num in range(2,n+1):
total *= num
return total
input:
n = 10
print(str(n) + "! = ", end = '')
def factorial(n):
'''
print factorial number in human way
'''
if n < 0:
return 'factorial() not defined for negative values'
if n == 0:
return 1
if n == 1:
print('', n, ' =', end = ' ')
return 1
else:
print('', n, '*', end = '')
return n * factorial(n - 1)
print(factorial(n))
output:
10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 3628800