Python recursion is very slow - python

I am a novice at python, but was surprised at how slow this recursive call took to execute:
def daH(m:int):
if m == 1:
return int(1)
else:
if m <= .5 * (daH(m-1) * (daH(m-1) +1)):
return int(daH(m-1))
else:
return int(daH(m-1) + 1)
print(daH(10)) # prints 4
print(daH(11)) # prints 5
print(daH(15)) # prints 5
print(daH(16)) # prints 6
print(daH(106)) # prints ??? (gave up waiting)
I ran it on IDLE, python 3.6. I added the INT stuff but it did not help. I had no problems running the standard factorial recursion and printing factorial(106).
Can this attempt at recursion be salvaged?

You are computing daH(m-1) 3 times, making the algorithm slower than necessary. Instead, calculate it just once and bind the result to a local variable. (Also, not necessary to cast to int)
def daH(m:int):
if m == 1:
return 1
else:
r = daH(m-1)
if m <= .5 * r * (r + 1):
return r
else:
return r + 1
Calling the function three times instead of once may not seem like much, but remember that those calls will stack exponentially! You call it three times, and each of those again call it three times, and so on. This results in a complexity of O(3m), which even for m=15 results in about 15 million recursive calls, as opposed to the 15 that are actually necessary.

Since daH is a mathematical function and always produces the same output for a given input, you can cache the values and use them.
Sample output
$ time python use_cache.py
4
5
5
6
15
real 0m0.194s
user 0m0.144s
sys 0m0.047s
$
Caching decorator
$ cat use_cache.py
def use_cache_when_possible(func):
cache = {}
def foo(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return foo
#use_cache_when_possible
def daH(m):
if m == 1:
return int(1)
else:
if m <= .5 * (daH(m-1) * (daH(m-1) +1)):
return int(daH(m-1))
else:
return int(daH(m-1) + 1)
print(daH(10))
print(daH(11))
print(daH(15))
print(daH(16))
print(daH(106))
$

You are calling daH unnecessary number of times.
Your code make 3 calls to daH(m-1) for every call daH(m), which makes exponential complexity, which scales badly, i.e performs badly on larger values of m.
You can save the result of call todaH and use it in places where you called daH with same input, instead of calling the function multiple times.
def daH(m:int):
if m == 1:
return 1
else:
ret_val = daH(m-1)
if m <= .5 * (ret_val * (ret_val +1)):
return ret_val
else:
return ret_val + 1

Related

How are recursive function calls in Python able to find keys in an empty dictionary?

So was playing around with memory_profiler module and noticed something weird. I was working on a function used to find nCr combinations using recursion. I made 2 version of of my function :
ver 1:
#profile
def c(n, r, memo={}):
pair = (n, r)
if pair in memo:
return memo[pair]
if r == 1:
return n
if n - r < r:
return c(n, n - r)
if r == 0 or r == n:
return 1
return c(n - 1, r - 1) + c(n - 1, r)
print(c(20, 10))
ver 2:
#profile
def c(n, r, memo={}):
pair = (n, r)
if pair in memo:
return memo[pair]
if r == 1:
return n
if n - r < r:
return c(n, n - r)
if r == 0 or r == n:
return 1
memo[pair] = c(n - 1, r - 1) + c(n - 1, r)
return memo[pair]
print(c(20, 10))
memory_profiler :
ver 1 :
ver 2 :
As you can see ver 1 and ver 2 are the same except for the last 2 lines of the function. But running memory_profiler on them they have great difference in performance. The thing I'm most puzzled by is although I forgot to pass memo in the function call it still results in such great performance improvement.
Each function call results in a new empty memo dict for that function. So why does line 8 in version 1 evaluates as False and fails to return whereas the same line in ver 2 evaluates True and returns 36 times. Shouldn't they both realistically be false as I'm trying to find key in an empty dictionary ?
Shouldn't there be virtually no observable difference in performance ?
Your assumption that
Each function call results in a new empty memo dict for that function.
is incorrect. Unlike in JavaScript, in Python default parameter values are evaluated at definition time, once. If the c function is called without a value for the memo parameter, the instance of the dict constructed at definition time will be used as its value each time. At construction time it is empty, but subsequent invocations may mutate it. Observe the following:
def accumulate(a, b=[]):
b.append(a)
print(b)
accumulate(1)
accumulate('foo')
accumulate(None)
The above will print [1], then [1, 'foo'], then [1, 'foo', None].
Using default parameter values for memoisation is a pretty bad idea. Not only is it rather obscure and confusing, it also allows the caller to break the function by actually providing a value for the memo parameter. A better idea would be to use a global/non-local binding, or a decorator such as functools.cache or functools.lru_cache.

How to make a function memorize 2 variable function values?

I want to improve the performance of a recursive calculation of values
n = 100
def T(n,k):
q = 0
if n == 0 and k == 0:
return(1)
q = 1
if k>n or n<0:
return(0)
q = 1
if q != 1:
return(T(n-1,k-1)+n*T(n-1,k))
for i in range(n):
for n in range(i+1):
print(T(i,n))
print("*********")
However, I've only found ways to do this with functions that only take 1 argument, like this:
def mem(f):
memory = {}
def inner_function(x):
if x not in memory:
memory[x] = f(x)
return memory[x]
else:
return memory[x]
return inner_function
#mem
def fibonacci(n):
if n == 1 or n == 0:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
I was thinking of doing a 2d array, but I don't know yet (assuming it's possible) how the idea of doing so, with lists of lists, would help.
You can use functools.lru_cache for this
from functools import lru_cache
#lru_cache(maxsize=32)
def T(n,k):
q = 0
if n == 0 and k == 0:
return(1)
q = 1
if k>n or n<0:
return(0)
q = 1
if q != 1:
return(T(n-1,k-1)+n*T(n-1,k))
You can use this decorator to memoize function calls, with this function in particular this will save up to the maxsize most recent calls.
Note in this particular case, the vast majority of the time is actually spent writing to console because of your print statements. If you remove this (but still leave your T(i,n) invocation) your code will complete almost instantaneously.
You can easily extend your mem decorator to work with variable *args parameters, looking them up in the memory. This would work with **kwargs, too, you'd have to convert them to a hashable type though, e.g. frozenset of tuples. And of course all parameters must be hashable for this.
def mem(f):
memory = {}
def inner_function(*args):
if args not in memory:
memory[args] = f(*args)
return memory[args]
return inner_function
Tested with your T function, works fine. In practice, however, you might still want to use functools.lru_cache.

What is the most computationally efficient method to recursively calculate the Fibonacci sequence?

Here is the code I currently have.
def fibonacci(n):
if n == 1:
return 1
elif n == 2:
return 1
else:
value = fibonacci(n - 1) + fibonacci(n - 2)
return value
This currently takes quite some time to calculate values greater than n = 30. Is there a more computationally efficient method to accomplish this?
Adding a value cache to trade some memory for a reduced processing time can be a useful method. A purely recursive program will attempt to calculate values over and over again, however this takes time for larger values. If the values do not change, then storing them can be helpful. It is important to note, however, that should values be volatile you might need a different approach.
fibonacci_value_cache = {}
def fibonacci(n):
if n == 1:
return 1
elif n == 2:
return 1
elif n in fibonacci_value_cache:
return fibonacci_value_cache[n]
else:
fibonacci_value_cache[n] = fibonacci(n - 1) + fibonacci(n - 2)
return fibonacci_value_cache[n]
n = 100
print("Fib " + str(n) + ": " + str(fibonacci(n)))
Here, we check if the value is in the dictionary and return it if it is, otherwise we calculate it and add it to the dictionary. This means that we are make better use of the processor by not calculating the same value multiple times.
There's a recipe for a decorator that uses as an example exactly what you want. It's named Memoize in the PythonDecoratorLibrary.
It may seem like overkill, but having the memoized decorator around could be useful for other future tasks. That said, here it is in its entirety (although I changed the print at the end):
import collections
import functools
class memoized(object):
'''Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
(not reevaluated).
'''
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if not isinstance(args, collections.Hashable):
# uncacheable. a list, for instance.
# better to not cache than blow up.
return self.func(*args)
if args in self.cache:
return self.cache[args]
else:
value = self.func(*args)
self.cache[args] = value
return value
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
def __get__(self, obj, objtype):
'''Support instance methods.'''
return functools.partial(self.__call__, obj)
#memoized
def fibonacci(n):
"Return the nth fibonacci number."
if n in (0, 1):
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(12))
Using idea of Dynamic Programming, and store the intermediate results to save computational cost, it could be very efficient. The code below cost less than 0.02s for n=10000 on my laptop.
def fib(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
for i in range(n):
result.append(b)
a, b = b, a + b
return result
No need for caching/memoization. Here's a Python 3 implementation that expresses the Fibonacci sequence as powers of a matrix, then does efficient exponentiation via halving and squaring. The result is O(log n) in both time and storage.
def matrix_fib(n):
if n == 1:
return [0,1]
else:
f = matrix_fib(n // 2)
c = f[0] * f[0] + f[1] * f[1]
d = f[1] * (f[1] + 2 * f[0])
return [c,d] if (n & 1) == 0 else [d,c+d]
def fib(n):
return n if n == 0 else matrix_fib(n)[1]
print(fib(1000000))
On my laptop this coughs up the value of the millionth Fibonacci number in a little over half a second, and the bulk of that is probably in the big integer arithmetic and formatting of the output—the result is ridiculously large. You don't need to worry about stack overflow, though. The call stack depth for this is only log2(1000000) = 20.

How can I pass functions or operators as arguments to a function in Python?

...while still leaving it executable within the function.
The idea behind this is I want to create a summation function. Here's what I have so far:
def summation(n, bound, operation):
if operation is None and upper != 'inf':
g = 0
for num in range(n, limit + 1):
g += num
return g
else:
pass
But summations are most often about infinite convergent series (for which I use 'inf'), with operations applied to each term. Ideally, I'd like to be able to write print summation(0, 'inf', 1 / factorial(n)) and get the mathematical constant e, or def W(x): return summation(1, 'inf', ((-n) ** (n - 1)) / factorial(n)) to get the Lambert W function.
All that comes to my mind is passing the appropriate arithmetic as a string and then using the exec statement to execute it. But I don't think that would accomplish the whole thing, and it's obviously dangerous to use exec with possibly user-entered code.
In Python, functions are first-class, which is to say they can be used and passed around like any other values, so you can take a function:
def example(f):
return f(1) + f(2)
To run it, you could define a function like this:
def square(n):
return n * n
And then pass it to your other function:
example(square) # = square(1) + square(2) = 1 + 4 = 5
You can also use lambda to avoid having to define a new function if it's a simple expression:
example(lambda n: n * n)

Calculate nth term of Fibonacci sequence in Python

The following code is to calculate nth term og fibonacci sequence in python using matrix exponentiation for various test cases t.But the program gives absurd output.Please tell me where i am wrong.when i ran the code in C++ it runs perfectly.
class matrix:
def __init__(self):
self.a=self.b=self.c=1
self.d=0
def mul(self,e,f):
ret = matrix()
ret.a=(e.a*f.a)+(e.b+f.c)
ret.b=(e.a*f.b)+(e.b+f.d)
ret.c=(e.c*f.a)+(e.d+f.c)
ret.d=(e.c*f.b)+(e.d+f.d)
return ret
def exp(self,a,p):
if(p==0):
temp=matrix()
temp.a=temp.b=temp.c=temp.d=1
return temp
if(p==1):
return a
if(p%2==0):
return self.exp(self.mul(a,a),p/2)
else:
return self.mul(a,self.exp(self.mul(a,a),(p-1)/2))
def fib(self,n):
if (n==0):
return 0
if (n==1):
return 1
s=matrix()
s=self.exp(s,n)
return s.d
t=int(raw_input())
while(t>0):
v=matrix()
n=int(raw_input())
print v.fib(n)
t=t-1
The problem lies in your __init__ function. In python the so-called variables are just 'tags' to data in the memory. To compare with C/C++, these can be thought of as pointers. when you assign self.a = self.b = self.c, you are basically assigning three different names to the same data in the memory. Any change you make in a will be reflected back in b and c and so on.
For your problem where you need three separate variables, one way to change the __init__ function is like:
self.a, self.b, self.c = 1, 1, 1
or you can use copy. copy() tells python to assign a new memory location and then assign the tag on the right hand side to that location. For more read the official documentation on this http://docs.python.org/2/library/copy.html. You can also read a short walk-through on this in Python Tutorial: Shallow and Deep-copy
There are several issues, in order of importance:
1) Your multiplication is wrong. Note the multiplications at the right where you have sums):
def mul(self,e,f):
ret = matrix()
ret.a=(e.a*f.a)+(e.b*f.c)
ret.b=(e.a*f.b)+(e.b*f.d)
ret.c=(e.c*f.a)+(e.d*f.c)
ret.d=(e.c*f.b)+(e.d*f.d)
return ret
2) In the last line, you do return s.d but you should return s.b or s.c or you will get one less fibonacci.
3) The line temp.a=temp.b=temp.c=temp.d=1 is not necessary because the constructor does the work. Besides it is wrong, because d should be 0.
4) Why are mul and exp class functions if they don't use self. It does no harm but they should be #staticmethod
5) Again, it does no harm but your second recursive call is unnecessarily complex. Just write:
return matrix.mul(a,matrix.exp(a, p-1))
I'm not sure if it is required for you to use matrix exponentiation for this problem. Unfortunately, I do not know much about Python classes quite yet. However, the following code does what the question heading wants: to find the n-th Fibonacci number. Below I describe this as F_n. Note the initial conditions for low values of n.
def fibN( n ):
"""
fibonacci: int -> int
Returns F_n.
Note: F_1 = 0, F_2 = 1, F_3 = 1, F_4 = 2
"""
n = abs( int( n ))
if n == 0:
fib = 0
elif n == 1:
fib = 1
else:
counter = 2
f0 = 0
f1 = 1
fib = f0 + f1
while counter <= n:
fib = f0 + f1
f0 = f1
f1 = fib
counter += 1
return fib

Categories