472,142 Members | 1,264 Online

Why is this blowing the stack, thought it was tail-recursive...

def fib(n):
def fibt(a, b, n):
if n <= 1:
return b
else:
return fibt(b, a + b, n - 1)
if n == 0:
return 0
else:
return fibt(0, 1, n);
and can memoization speed up this even more? tesintg with memoization
doesnt really say anything because it is so fast it is instant anyway.
(the classic easy-redable exponential fib-function can obv be helped
enormously with memoization though.)
Jul 12 '08 #1
6 1191
Why is this blowing the stack, thought it was tail-recursive...
Because python does no tail-call optimization.

Ciao
Marc
Jul 12 '08 #2
ssecorp wrote:
<a recursive Fibonacci generator thast demonstrates no tail recursion
used>
and can memoization speed up this even more? ....
Generators get you to an even clearer Fibonacci expression.

def _Fibonacci_gen():
a, b = 1, 0
while True:
a += b
yield a
b += a
yield b

Here's how to use it to avoid redundant comutation:

_next_entry = _Fibonacci_gen().next
_sofar = []

def fib(n):
while len(_sofar) <= n:
_sofar.append(_next_entry())
return _sofar[n]
--Scott David Daniels
Sc***********@Acm.Org
Jul 12 '08 #3

ssecorp wrote:
def fib(n):
def fibt(a, b, n):
if n <= 1:
return b
else:
return fibt(b, a + b, n - 1)
if n == 0:
return 0
else:
return fibt(0, 1, n);

and can memoization speed up this even more? tesintg with memoization
doesnt really say anything because it is so fast it is instant anyway.
Except for the fact that a+b gets slower as a and b get bigger, this
would already be linear time in n. Memoization (here by means of a
linear list) only helps if the list is preserved and one makes repeated
requests for various fib values.

I am just curious what input you tried that blew the stack? It had to
be pretty large.

The stack problem, by the way, is one reason linear induction is usually
written in Python with iteration syntax instead of recursion syntax.
Another is the extra simplicity.

def fib(n):
a,b = 1,0
while n:
a,b,n = b, a+b, n-1
return b

A third is the ease of conversion to a (space-efficient) generator function.

def fib_gen()
a,b = 1,0
while True:
yield b
a,b = b, a+b

The generator it produces can be used, for instance, to fill a list
(linear memo) 'on demand'.

A model that leads to the linear algorithm (as opposed to the double
recursion derived from Fibonacci's rabbit model) is as follows:
A population consists of juveniles and adults. In one period, juveniles
become adults (which never die) and adults birth (or bud off) one
juvenile. (Yeast are somewhat like this.) At time 0, we start with 1
juvenile and 0 adults. How many adults are there at time n?

Terry Jan Reedy

Jul 12 '08 #4
On Sat, 12 Jul 2008 19:25:18 -0400, Terry Reedy wrote:
ssecorp wrote:
>def fib(n):
def fibt(a, b, n):
if n <= 1:
return b
else:
return fibt(b, a + b, n - 1)
if n == 0:
return 0
else:
return fibt(0, 1, n);

and can memoization speed up this even more? tesintg with memoization
doesnt really say anything because it is so fast it is instant anyway.

Except for the fact that a+b gets slower as a and b get bigger, this
would already be linear time in n. Memoization (here by means of a
linear list) only helps if the list is preserved and one makes repeated
requests for various fib values.

I am just curious what input you tried that blew the stack? It had to
be pretty large.
No, not really. Try it for yourself: on my system, I get RuntimeError:
maximum recursion depth exceeded with fib(999).

fib(999) is a large number, with 208 digits, but not that large:

26863810024485359386146727202142923967616609318986 9523401
23175997617981700247881689338369654483356564191827 8561614
43356312976673642210350324634850410377680367334151 1728991
69723197082763985615764450078474174626L
--
Steven
Jul 13 '08 #5
On Jul 13, 7:56*am, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.auwrote:
On Sat, 12 Jul 2008 19:25:18 -0400, Terry Reedy wrote:
ssecorp wrote:
def fib(n):
* * def fibt(a, b, n):
* * * * if n <= 1:
* * * * * * return b
* * * * else:
* * * * * * return fibt(b, a + b, n - 1)
* * if n == 0:
* * * * return 0
* * else:
* * * * return fibt(0, 1, n);
and can memoization speed up this even more? tesintg with memoization
doesnt really say anything because it is so fast it is instant anyway.
Except for the fact that a+b gets slower as a and b get bigger, this
would already be linear time in n. *Memoization (here by means of a
linear list) only helps if the list is preserved and one makes repeated
requests for various fib values.
I am just curious what input you tried that blew the stack? *It had to
be pretty large.

No, not really. Try it for yourself: on my system, I get RuntimeError:
maximum recursion depth exceeded with fib(999).

fib(999) is a large number, with 208 digits, but not that large:

26863810024485359386146727202142923967616609318986 9523401
23175997617981700247881689338369654483356564191827 8561614
43356312976673642210350324634850410377680367334151 1728991
69723197082763985615764450078474174626L

--
Steven
The default CPython recursion limit is 1000 - so hitting it with an
input of 999 is not that surprising...

You can set a higher limit with sys.setrecursionlimit(...)

Michael Foord
http://www.ironpythoninaction.com/
Jul 13 '08 #6
On Jul 13, 8:44*pm, Fuzzyman <fuzzy...@gmail.comwrote:
On Jul 13, 7:56*am, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.auwrote:
On Sat, 12 Jul 2008 19:25:18 -0400, Terry Reedy wrote:
ssecorp wrote:
>def fib(n):
>* * def fibt(a, b, n):
>* * * * if n <= 1:
>* * * * * * return b
>* * * * else:
>* * * * * * return fibt(b, a + b, n - 1)
>* * if n == 0:
>* * * * return 0
>* * else:
>* * * * return fibt(0, 1, n);
>and can memoization speed up this even more? tesintg with memoization
>doesnt really say anything because it is so fast it is instant anyway.
Except for the fact that a+b gets slower as a and b get bigger, this
would already be linear time in n. *Memoization (here by means of a
linear list) only helps if the list is preserved and one makes repeated
requests for various fib values.
I am just curious what input you tried that blew the stack? *It hadto
be pretty large.
No, not really. Try it for yourself: on my system, I get RuntimeError:
maximum recursion depth exceeded with fib(999).
fib(999) is a large number, with 208 digits, but not that large:
26863810024485359386146727202142923967616609318986 9523401
23175997617981700247881689338369654483356564191827 8561614
43356312976673642210350324634850410377680367334151 1728991
69723197082763985615764450078474174626L
--
Steven

The default CPython recursion limit is 1000 - so hitting it with an
input of 999 is not that surprising...

You can set a higher limit with sys.setrecursionlimit(...)
Though that would be a kludge, not a fix. Using iteration or generator
expression is better.

Jul 16 '08 #7