I have this tail recursive function here:
def fib(n, sum):
if n < 1:
return sum
else:
return fib(n-1, sum+n)
c = 998
print(fib(c, 0))
It works up to n=997, then it just breaks and spits a "maximum recursion depth exceeded in comparison" RuntimeError
. Is this just a stack overflow? Is there a way to get around it?
fib
? Were you intending to use this as a helper to calculate the Fibonacci sequence? If so, what's your approach - yoniLavi 2018-03-10 21:46
It is a guard against a stack overflow, yes. Python (or rather, the CPython implementation) doesn't optimize tail recursion, and unbridled recursion causes stack overflows. You can change the recursion limit with sys.setrecursionlimit
, but doing so is dangerous -- the standard limit is a little conservative, but Python stackframes can be quite big.
Python isn't a functional language and tail recursion is not a particularly efficient technique. Rewriting the algorithm iteratively, if possible, is generally a better idea.
sys
and the resource
modules: http://stackoverflow.com/a/16248113/20552 - Thomas Ahle 2014-04-28 19:10
sys.setrecursionlimit
? What exactly would happen if I only set sys.setrecusionlimit
, but not touch resource
- max 2016-10-08 07:07
The answer probably means "in Python specifically" but that isn't what it say - Peter R 2017-03-06 15:04
Looks like you just need to set a higher recursion depth
sys.setrecursionlimit(1500)
It's to avoid a stack overflow. The Python interpreter limits the depths of recursion to help you avoid infinite recursions, resulting in stack overflows. Try increasing the recursion limit (sys.setrecursionlimit) or re-writing your code without recursion.
from python website:
sys.getrecursionlimit()
Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python. It can be set by setrecursionlimit().
sys.getrecursionlimit()
, and that was half of the original question - rob3c 2017-11-03 18:12
Use a language that guarantees tail-call optimisation. Or use iteration. Alternatively, get cute with decorators.
I realize this is an old question but for those reading, I would recommend against using recursion for problems such as this - lists are much faster and avoid recursion entirely. I would implement this as:
def fibonacci(n):
f = [0,1,1]
for i in xrange(3,n):
f.append(f[i-1] + f[i-2])
return 'The %.0fth fibonacci number is: %.0f' % (n,f[-1])
(Use n+1 in xrange if you start counting your fibonacci sequence from 0 instead of 1.)
Of course Fibonacci numbers can be computed in O(n) by applying the Binet formula:
from math import floor, sqrt
def fib(n):
return int(floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))+0.5))
As the commenters note it's not O(1) but O(n) because of 2**n
. Also a difference is that you only get one value, while with recursion you get all values of Fibonacci(n)
up to that value.
n
because of floating point imprecision - the difference between (1+sqrt(5))**n
and (1+sqrt(5))**(n+1)
becomes less than 1 ulp, so you start getting incorrect results - Mego 2016-07-07 14:02
(1+sqrt(5))**n
and ((1+sqrt(5))**n)+1
that becomes less than 1 ulp! (small typo) Also, {@}rwst That's not O(1)! Calculating 2**n
takes at least O(n) time - user202729 2018-01-05 01:43
2**n
is effectively O(log(n)) using Exponentiattion by squaring - Sam 2018-02-18 18:02
I had a similar issue with the error "Max recursion depth exceeded". I discovered the error was being triggered by a corrupt file in the directory I was looping over with os.walk. If you have trouble solving this issue and you are working with file paths, be sure to narrow it down, as it might be a corrupt file.
resource.setrlimit
must also be used to increase the stack size and prevent segfault
The Linux kernel limits the stack of processes.
Python stores local variables on the stack of the interpreter, and so recursion takes up stack space of the interpreter.
If the Python interpreter tries to go over the stack limit, the Linux kernel segfaults it.
The stack limit size is controlled with the getrlimit
and setrlimit
system calls.
Python offers access to those system calls through the resource
module.
import resource
import sys
print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print
# Will segfault without this line.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)
def f(i):
print i
sys.stdout.flush()
f(i + 1)
f(0)
Of course, if you keep increasing ulimit, your RAM will run out, which will either slow your computer to a halt due to swap madness, or kill Python via the OOM Killer.
From bash, you can see and set the stack limit (in kb) with:
ulimit -s
ulimit -s 10000
Default value for me is 8Mb.
See also:
Tested on Ubuntu 16.10, Python 2.7.12.
rlimit_stack
after Stack Clash remediations may result in failure or related problems. Also see Red Hat Issue 1463241jww 2017-06-21 16:35
If you often need to change the recursion limit (e.g. while solving programming puzzles) you can define a simple context manager like this:
import sys
class recursionlimit:
def __init__(self, limit):
self.limit = limit
self.old_limit = sys.getrecursionlimit()
def __enter__(self):
sys.setrecursionlimit(self.limit)
def __exit__(self, type, value, tb):
sys.setrecursionlimit(self.old_limit)
Then to call a function with a custom limit you can do:
with recursionlimit(1500):
print(fib(1000, 0))
On exit from the body of the with
statement the recursion limit will be restored to the default value.
Use generators?
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fibs = fib() #seems to be the only way to get the following line to work is to
#assign the infinite generator to a variable
f = [fibs.next() for x in xrange(1001)]
for num in f:
print num
above fib() function adapted from: http://intermediatepythonista.com/python-generators
[fibs().next() for ...]
would make a new generator each time - tox123 2016-08-10 19:02
If you want to get only few Fibonacci numbers, you can use matrix method.
from numpy import matrix
def fib(n):
return (matrix('0 1; 1 1', dtype='object') ** n).item(1)
It's fast as numpy uses fast exponentiation algorithm. You get answer in O(log n). And it's better than Binet's formula because it uses only integers. But if you want all Fibonacci numbers up to n, then it's better to do it by memorisation.
I've used the matrix soluction for some problems. It is the best solution when you need a very large fibonacci number and you can't use a modulus.
If you are allowed to use a modulus, the pisano period the better way to do it - mentatkgs 2018-06-09 01:49
Many recommend that increasing recursion limit is a good solution however it is not because there will be always limit. Instead use an iterative solution.
def fib(n):
a,b = 1,1
for i in range(n-1):
a,b = b,a+b
return a
print fib(5)
As @alex suggested, you could use a generator function to do this. Here's the equivalent of the code in your question:
def fib(n):
def fibseq(n):
""" Iteratively return the first n Fibonacci numbers, starting from 0 """
a, b = 0, 1
for _ in xrange(n):
yield a
a, b = b, a + b
return sum(v for v in fibseq(n))
print format(fib(100000), ',d') # -> no recursion depth error