fibonacci program hangs after 40th term - python

I am trying to solve this problem
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
Link: https://projecteuler.net/problem=2
Below is the code I have to calculate a fibonacci number:
# fibonacci series
def fib(x):
if x==0 or x==1:
return 1
else:
return fib(x-1) + fib(x-2)
def test_fib(n):
for i in range (n+1):
print 'fib of', i, ' = ' , fib(i)
test_fib(41)
However, the program hangs after 40th term. What is the problem with this code and how to solve this problem for 40th and beyond terms?

A naive recursive implementation of the fibonacci algorithm will get slow really fast. There's two ways you can resolve this:
a) Switch to an iterative version
def fib(x):
if x==0 or x==1:
return 1
a = b = 1
for _ in range(x-1):
a, b = b, a+b
return b
This is less elegant than the recursive solution, but has linear time complexity.
b) Use memoization:
from functools import lru_cache
#lru_cache()
def fib(x):
if x==0 or x==1:
return 1
else:
return fib(x-1) + fib (x-2)
This is the recursive solution, but with a "memory". It has the added benefit of being even faster than the iterative version if you have to call the function many times.
In old versions of Python (e.g. 2.7), lru_cache didn't exist yet, but you implement a cheap copy that's enough for our use:
def lru_cache():
# second-order decorator to be a drop-in replacement
def decorator(fn):
cache = {}
def wrapped(*args, **kwargs):
if args in cache:
return cache[args]
val = fn(*args)
cache[args] = val
return val
return wrapped
return decorator

considering the terms in the Fibonacci sequence whose values do not
exceed four million
The 34th term exceeds four million, so you don't need beyond the 40th term. Problem solved.
A naive recursive implementation of the fibonacci algorithm will get
slow really fast. There's two ways you can resolve this:
A third approach is to use a recursive solution that isn't naive. One problem with your original is it's doubly recursive:
return fib(x-1) + fib(x-2)
Let's reduce that to a single recursion:
def fib(n, res=0, nxt=1):
if n == 0:
return res
return fib(n - 1, nxt, res + nxt)
This gets you past the 40th term, bottoming out recursion-wise at the 997th. If you need to go further, consider either #L3viathan's iteration or memoization solutions which are both excellent.

First of all here is a working Fibonacci number generator:
a,b = 0,1
print(a,b)
for x in range(0, 100):
a,b = b, a + b
print(b)
Next all you have to do is this:
a,b = 0,1
print(a,b)
for x in range(0, 100):
a,b = b, a + b
c = 0
if b % 2 == 0:
c = c + b
print(c)
The final iteration of c is your answer.

This will add your terms until a is bigger then 4 million:
def fibo(n):
i = 0
a = 1
b = 1
sumEven= 0
while i<n and a < 4000000:
print ("fib of ", i, " = ", a)
if(a % 2==0):
sumEven+=a
a,b = b, b+a
i+=1
print("sum of even: ", sumEven)
fibo(50)

Related

Recursion with memory vs loop

I've made two functions for computing the Fibonacci Sequence, one using recursion with memory and one using a loop;
def fib_rec(n, dic = {0 : 0, 1 : 1}):
if n in dic:
return dic[n]
else:
fib = fib_rec(n - 2, dic) + fib_rec(n - 1, dic)
dic[n] = fib
return fib
def fib_loop(n):
if n == 0 or n == 1:
return n
else:
smaller = 0
larger = 1
for i in range(1, n):
smaller, larger = larger, smaller + larger
return larger
I've heard that the Fibonacci Sequence often is solved using recursion, but I'm wondering why. Both my algorithms are of linear time complexity, but the one using a loop will not have to carry a dictionary of all past Fibonacci numbers, it also won't exceed Python's recursion depth.
Is this problem solved using recursion only to teach recursion or am I missing something?
The usual recursive O(N) Fibonacci implementation is more like this:
def fib(n, a=0, b=1):
if n == 0: return a
if n == 1: return b
return fib(n - 1, b, a + b)
The advantage with this approach (aside from the fact that it uses O(1) memory) is that it is tail-recursive: some compilers and/or runtimes can take advantage of that to secretly convert it to a simple JUMP instruction. This is called tail-call optimization.
Python, sadly, doesn't use this strategy, so it will use extra memory for the call stack, which as you noted quickly runs into Python's recursion depth limit.
The Fibonacci sequence is mostly a toy problem, used for teaching people how to write algorithms and about big Oh notation. It has elegant functional solutions as well as showing the strengths of dynamic programming (basically your dictionary-based solution), but it's also practically a solved problem.
We can also go a lot faster. The page https://www.nayuki.io/page/fast-fibonacci-algorithms describes how. It includes a fast doubling algorithm written in Python:
#
# Fast doubling Fibonacci algorithm (Python)
# by Project Nayuki, 2015. Public domain.
# https://www.nayuki.io/page/fast-fibonacci-algorithms
#
# (Public) Returns F(n).
def fibonacci(n):
if n < 0:
raise ValueError("Negative arguments not implemented")
return _fib(n)[0]
# (Private) Returns the tuple (F(n), F(n+1)).
def _fib(n):
if n == 0:
return (0, 1)
else:
a, b = _fib(n // 2)
c = a * (b * 2 - a)
d = a * a + b * b
if n % 2 == 0:
return (c, d)
else:
return (d, c + d)

This program i am creating for Fibonacci but it return none.Kindly solve this problem

def fibonacci(n):
for i in range(n,1):
fab=0
if(i>1):
fab=fab+i
i=i-1
return fab
elif i==0:
return 0
else:
return 1
n1 = int(input("enter the nth term: "))
n2=fibonacci(n1)
print(n2)
The only way your code can return none is if you enter an invalid range, where the start value is greater than or equal to the stop value (1)
you probably just need range(n) instead of range(n, 1)
You can do this too:
def fibonacci(n):
return 0 if n == 1 else (1 if n == 2 else (fibonacci(n - 1) + fibonacci(n - 2) if n > 0 else None))
print(fibonacci(12))
You may need to use recursion for for nth Fibonacci number:
ex:
def Fibonacci(n):
if n==1:
return 0
elif n==2:
return 1
else:
return Fibonacci(n-1)+Fibonacci(n-2)
print(Fibonacci(9))
# output:21
If you do not plan to use large numbers, you can use the easy and simple typical recursive way of programming this function, although it may be slow for big numbers (>25 is noticeable), so take it into account.
def fibonacci(n):
if n<=0:
return 0
if n==1:
return 1
return fibonacci(n-1)+fibonacci(n-2)
You can also add a cache for the numbers you already stepped in, in order to make it run much faster. It will consume a very small amount of extra memory but it allows you to calculate larger numbers instantaneously (you can test it with fibonacci(1000), which is almost the last number you can calculate due to recursion limitation)
cache_fib = {}
def fibonacci(n):
if n<=0:
return 0
if n==1:
return 1
if n in cache_fib.keys():
return cache_fib[n]
result = fibonacci(n-1)+fibonacci(n-2)
cache_fib[n] = result
return result
In case you really need big numbers, you can do this trick to allow more recursion levels:
cache_fib = {1:1}
def fibonacci(n):
if n<=0:
return 0
if n in cache_fib.keys():
return cache_fib[n]
max_cached = max(cache_fib.keys())
if n-max_cached>500:
print("max_cached:", max_cached)
fibonacci(max_cached+500)
result = fibonacci(n-1)+fibonacci(n-2)
cache_fib[n] = result
return result
range(n,1) creates a range starting with n, incrementing in steps of 1 and stopping when n is larger or equal to 1. So, in case n is negative or zero, your loop will be executed. But in case n is 1 or larger, the loop will never be executed and the function just returns None.
If you would like a range going from n downwards to 1, you can use range(n,1,-1) where -1 is the step value. Note that when stepping down, the last number is included range(5,1,-1) is [5,4,3,2,1] while when stepping upwards range(1,5) is [1,2,3,4] the last element is not included. range(n) with only one parameter also exists. It is equivalent to range(0, n) and goes from 0 till n-1, which means the loop would be executed exactly n times.
Also note that you write return in every clause of the if statement. That makes that your function returns its value and interrupts the for loop.
Further, note that you set fab=0 at the start of your for loop. This makes that it is set again and again to 0 each time you do a pass of the loop. Therefore, it is better to put fab=0 just before the start of the loop.
As others have mentioned, even with these changes, your function will not calculate the Fibonacci numbers. A recursive function is a simple though inefficient solution. Some fancy playing with two variables can calculate Fibonacci in a for loop. Another interesting approach is memorization or caching as demonstrated by #Ganathor.
Here is a solution that without recursion and without caching. Note that Fibonacci is a very special case where this works. Recursion and caching are very useful tools for more real world problems.
def fibonacci(n):
a = 0
b = 1
for i in range(n):
a, b = a + b, a # note that a and b get new values simultaneously
return a
print (fibonacci(100000))
And if you want a really, really fast and fancy code:
def fibonacci_fast(n):
a = 1
b = 0
p = 0
q = 1
while n > 0 :
if n % 2 == 0 :
p, q = p*p + q*q, 2*p*q + q*q
n = n // 2
else:
a, b = b*q + a*q + a*p, b*p + a*q
n = n - 1
return b
print (fibonacci_fast(1000000))
Note that this relies on some special properties of the Fibonacci sequence. It also gets slow for Python to do calculations with really large numbers. The millionth Fibonacci number has more than 200,000 digits.

How can I make this python function run O(log n) time instead of O(n) time?

def findMax(f,c):
n=1
while f(n) <= c:
n += 1
return n
This is a higher-order python function that given function f and a maximal count,
c returns the largest n such that f(n) ≤ c. This works but not when n gets too large for e.g f(10**6). How can I make this algorithm run O(log n) time so it can facilitate f(10**6) using the function below?
def f(n):
return math.log(n, 2)
Change n += 1 to n *= 2 for logarithmic outcome.
Logarithmic sequences increment in multiples of 2, and are non-linear, thus logarithmic sequences don't increment by 1.
Use a search algorithm to find the solution faster. This is an implementation using jpe.math.framework.algorythems.brent which is an implementation of the brent search algorithm.
import math
import jpe
import jpe.math.framework.algorythems
def f(x):
return math.log(x, 2)
value = 9E2
startVal = 1E300
val = int(jpe.math.framework.algorythems.brent(f, a=0, b=startVal, val=value, mode=jpe.math.framework.algorythems.modes.equalVal)[0])#takes 37 iters
print(val)
Alternatively in this scenario with this f:
the result is within 1 of 2**c (c as passedto findMax)

Python - Modify dictionary from function

Okay, this question is a little strange, but I was wondering if I could do it like this.
I'm working on a simple Fibonacci number generator for fun since I was interested in programming it.
So I wrote this out:
def f(n):
if n == 1: return 1
if n == 2: return 2
else:
return f(n-1) + f(n-2)
and that ran very slowly, taking 15 seconds to do f(30) on my computer.
So then I wrote this:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) + f(n-2)
a["fib(%s)" % n] = z
return z
which basically stores previous results in a dictionary like so:
{'f(1)':1,'f(2)':2,'f(3)':3,'f(4)':5} and so on. In the function it would check if that result was in that dictionary, then just use that instead of having to redo all the calculations.
This made it tons faster. I could do f(100) and it instantly appear. Going by intervals of 500, I got to f(4000) and it was still instantaneous. The one problem was that the dictionary was getting stupidly big.
So I added a = {} to the end of the function, and that didn't work; it still left a as a massive dict.
So doing this:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) + f(n-2)
a["fib(%s)" % n] = z
return z
a = {}
didn't work. but if I do this:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) + f(n-2)
a["fib(%s)" % n] = z
return z
# now run the function
f(100)
a = {}
a gets reset to an empty dictionary. Why does this happen and how can I fix it?
Your a = {} statement inside the function was never being executed; every possible path of execution reaches a return before then. If it WAS executed, you wouldn't have liked the results - it would have executed in every single recursive call to the function, meaning that your dictionary would never hold more than one item! You would somehow have to detect the outermost call and only clear the dict there, or (much simpler) clear it outside of the recursion as in your second example.
Note that much of the size of your dictionary is coming from the strange decision to use a long string key. Keying it with the number itself (as in a[n] = z) would make it much more compact.
(For future reference: the technique you've come up with here, of saving the results from previous function calls, is known as "memoization".)
Despite your question, what you really want is a faster way of calculating Fibonacci sequence, right? The problem with your original approach is that recurrence, despite being very elegant and quick to code, is quite slow. Fibonacci sequence has a close form solution. You should do this math directly to speed up your code.
As convention, consider the Fibonacci sequence F(i) as: F(0) = 0, F(1) = 1, F(k) = F(k-1) + F(k-2) k = 2, 3, ... The solution for this sequence is (I will not demonstrate it here, because is not the place for that) F(k) = (1/sqrt(5))*(a^k - b^k), where a = (1 + sqrt(5))/2 and b = (1 - sqrt(5))/2.
Thus, you code could be implemented like this:
def f(n):
a = (1 + 5**.5)/2
b = (1 - 5**.5)/2
F = (a**n - b**n)/5**.5
F = int(round(F)) #necessary to get an integer without the decimal part of the approximation. Afterall, you are working with irrational numbers.
return F
This code scale very well for large values of n.

Recursive function to calculate sum of 1 to n?

This is what I've got and I'm not sure why it's not working:
def sum(n):
if n > 0:
print(n)
return sum(n) + sum(n-1)
else:
print("done doodly")
number = int(input(": "))
sum(number)
For example if the user inputs 5, I want the program to calculate the sum of 5+4+3+2+1. What am I doing wrong?
For the non-recursive version of this question, see Sum of the integers from 1 to n
Two things:
Calling sum(n) when computing sum for n won't do you much good because you'll recurse indefinitely. So the line return sum(n)+sum(n-1) is incorrect; it needs to be n plus the sum of the n - 1 other values. This also makes sense as that's what you want to compute.
You need to return a value for the base case and for the recursive case.
As such you can simplify your code to:
def sum(n):
if n == 0:
return 0
return n + sum(n - 1)
You forgot to return when n==0 (in your else)
>>> def Sum(n):
... if not n:
... return 0
... else:
... return n + Sum(n-1)
...
>>> Sum(5)
15
Recursion is a wrong way to calculate the sum of the first n number, since you make the computer to do n calculations (This runs in O(n) time.) which is a waste.
You could even use the built-in sum() function with range(), but despite this code is looking nice and clean, it still runs in O(n):
>>> def sum_(n):
... return sum(range(1, n+1))
...
>>> sum_(5)
15
Instead recursion I recommend using the equation of sum of arithmetic series, since It runs in O(1) time:
>>> def sum_(n):
... return (n + n**2)//2
...
>>> sum_(5)
15
You can complicate your code to:
def my_sum(n, first=0):
if n == first:
return 0
else:
return n + my_sum(n-1, (n+first)//2) + my_sum((n+first)//2, first)
The advantage is that now you only use log(n) stack instead of nstack
I think you can use the below mathematical function(complexity O(1)) instead of using recursion(complexity O(n))
def sum(n):
return (n*(n+1))/2
Using Recursion
def sum_upto(n):
return n + sum_upto(n-1) if n else 0
sum_upto(100)
5050
Please have a look at the below snippet in regards to your request. I certainly hope this helps. Cheers!
def recursive_sum(n):
return n if n <= 1 else n + recursive_sum(n-1)
# Please change the number to test other scenarios.
num = 100
if num < 0:
print("Please enter a positive number")
else:
print("The sum is",recursive_sum(num))

Categories