Hi All,
Why recursive function consumes more processing time than loops?
And, Is it cumbersome for the processor?
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
print fibonacci(40)
^^^^ this example takes about 1 min, but with loop only few ms
I assume your loop is not written identically.
Recursion means a function call. A function call will take more time and memory than just executing a loop. But the difference isn't huge.
But your program is written in a way where you're redoing your work over and over and over. I'm assuming your loop was not written that way. The slowness is due to the algorithm, not inherently the recursion.
If you add a memoization cache to it, it will complete quickly.
from functools import lru_cache
@lru_cache
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(40))
(May-11-2021, 11:50 PM)bowlofred Wrote: [ -> ]If you add a memoization cache to it, it will complete quickly.
Thank You alot
(May-11-2021, 11:50 PM)bowlofred Wrote: [ -> ]from functools import lru_cache
i can't import that coz i'm using v2.7
Python 2 is very old and there's lots of things that no longer support it. You should figure out what is preventing you from using python 3 and try to address it.
I ran this code
def fib(n):
fib.count += 1
if n < 2:
return 1
else:
return fib(n-2) + fib(n-1)
fib.count = 0
print(fib(40))
print(fib.count)
The fib function was called 331,160,281 times
Then I ran this code that uses a dictionary to save and reuse partial results:
def fib(n):
fib.count += 1
a = partial_results.get(n, None)
if a is None:
a = fib(n-2) + fib(n-1)
partial_results[n] = a
return a
partial_results = {0:0, 1:1}
fib.count = 0
print(fib(40))
print(fib.count)
This is similar to what Python does for you using the cache, but expanded out so you can see the mechanism. The result was the same, but the fib function is only called 79 times. If I were to compute multiple Fibonacci numbers many of them could be calculated by a single call to fib.
So raw recursion takes longer because it is doing a ridiculous number of function calls calculating the same results over and over and over.
(May-12-2021, 03:06 AM)deanhystad Wrote: [ -> ]The fib function was called 331,160,281 times
I'm shocked.
trying to understand from where this huge number come!
Starting with a really simple case:
fib[4] = fib[3] + fib[2]
fib[4] = (fib[3] = fib[2] + fib[1]) + (fib[2] = fib[1] + fib[0])
fib[4] = (fib[3] = (fib[2] = fib[1] + fib[0]) + fib[1]) + (fib[2] = fib[1] + fib[0])
That's 9 calls to fib().
This code records the number of times fib[n] is called for each value of n:
calls = {}
def fib(n):
calls[n] = calls.get(n, 0) + 1
if n < 2:
return 1
else:
return fib(n-1) + fib(n-2)
fib(5)
for n, count in calls.items():
print(f'{n:3d} : {count:5d}')
print('Total', sum(calls.values()))
Output:
5 : 1
4 : 1
3 : 2
2 : 3
1 : 5
0 : 3
Total 15
Just doubling starting n from 5 to 10 does this:
Output:
10 : 1
9 : 1
8 : 2
7 : 3
6 : 5
5 : 8
4 : 13
3 : 21
2 : 34
1 : 55
0 : 34
Total 177
And this is the horrible thing that happens when n = 40
Output:
40 : 1
39 : 1
38 : 2
37 : 3
36 : 5
35 : 8
34 : 13
33 : 21
32 : 34
31 : 55
30 : 89
29 : 144
28 : 233
27 : 377
26 : 610
25 : 987
24 : 1597
23 : 2584
22 : 4181
21 : 6765
20 : 10946
19 : 17711
18 : 28657
17 : 46368
16 : 75025
15 : 121393
14 : 196418
13 : 317811
12 : 514229
11 : 832040
10 : 1346269
9 : 2178309
8 : 3524578
7 : 5702887
6 : 9227465
5 : 14930352
4 : 24157817
3 : 39088169
2 : 63245986
1 : 102334155
0 : 63245986
Total 331160281
(May-17-2021, 06:29 PM)deanhystad Wrote: [ -> ]Starting with a really simple case:
that makes sense now, i benefited a lot from you
thank you
This is why using recursion shouldn't be taken for granted. The use of resources can easily become exponential; and the more resources are used per function call, the worse it'll be.
While recursion can provide a simpler and more readable solution to a problem, you'll be able to achieve similar results if you use the right tools. For instance, this code:
def fib(n):
fibs = []
for i in range(n + 1):
if i < 2:
fibs.append(i)
else:
fibs.append(fibs[i - 2] + fibs[i - 1])
return fibs[-1] if n >= 0 else 0
Provides the same simple solution by making use of a list to save and use the results as we iterate from 0 to n (included). The memory footprint in this case is minimal (only one function call and a list), as we don't take additional steps to keep track of n-1 and n-2 in order to calculate and use these.
Quote:Why recursive function consumes more of processing time than loops?
Calling a function takes time and requires also resources. Iterating over something is faster than calling.
A call of a function is for a human very short, but if you do this one million times, you can recognize the difference.