5

I need some help in understanding the processing that happens here, so let´s say I call fib(5) I want the fibonacci 5, which is 8. But my brain in trying to understand the algorithm says it´s not. This is how i (wrongly) think:

return fib(4) + fib(3) // Stack frame 1
return fib(3) + fib(1) // Stack frame 2

now cause x is 1 fib(1), the conditional statement if x == 0 or x == 1: causes the recursion to end. Which according to my logic would become 3+1+4+3. Please correct my faulty logic.

def fib(x):
    """assumes x an int >= 0
       returns Fibonacci of x"""
    assert type(x) == int and x >= 0
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)
2
  • 1
    Just write down on a paper the first call and replace each recursive calls of the function with the valid return statement. What you probably don't understand is that return fib(x - 1) + fib( x - 2) calls twice your fib function, one with parameter x = current x decremented once, and the other decremented twice. You should also probably look into function arguments again, since this is a typical misunderstanding when using functions (at first). Commented Dec 1, 2012 at 10:36
  • 1
    Just put some print statement in your function and run it to see what it's doing. Commented Dec 1, 2012 at 10:38

3 Answers 3

6

Here is the full expansion of what happens:

fib(5) expands to fib(4)+fib(3)
  fib(4) expands to fib(3)+fib(2)
    fib(3) expands to fib(2)+fib(1)
      fib(2) expands to fib(1)+fib(0)
        fib(1) evaluates to 1
        fib(0) evaluates to 1
      fib(1) evaluates to 1
    fib(2) expands to fib(1)+fib(0)
      fib(1) evaluates to 1
      fib(0) evaluates to 1
  fib(3) expands to fib(2)+fib(1)
    fib(2) expands to fib(1)+fib(0)
      fib(1) evaluates to 1
      fib(0) evaluates to 1
    fib(1) evaluates to 1

If you count the ones, you get 8 as the answer.

Sign up to request clarification or add additional context in comments.

Comments

6

For all x greater than 1, the fib function calls itself twice:

  1. fib(5) becomes fib(4) + fib(3)
  2. and expands to (fib(3) + fib(2)) + (fib(2) + fib(1))
  3. and expands to ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + 1)
  4. expands to (((fib(1) + fib(0)) + 1) + (1 + 1)) + ((1 + 1) + 1)
  5. expands to (((1 + 1) + 1) + (1 + 1)) + ((1 + 1) + 1)

which sums up to 8.

Comments

1

You can use package invocation_tree to visualize the execution:

import invocation_tree as ivt  # see link above for install instructions

def fib(x):
    """assumes x an int >= 0
       returns Fibonacci of x"""
    assert type(x) == int and x >= 0
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)

tree = ivt.blocking()  # blocks execution and waits for <Enter> key press
print( tree(fib, 5) )

fib1.gif

As you can see that a lot of duplicate computations are performed. To speed things up you can cache the input and output of each function call so that when the the functions is called with the same input the matching output gets reused so that no computation is required.

import invocation_tree as ivt
from functools import cache

@cache
def fib(x):
    """assumes x an int >= 0
       returns Fibonacci of x"""
    assert type(x) == int and x >= 0
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)

tree = ivt.gif("fib.png")
print( tree(fib, 5) )

fib2.png

This technique is called memoization and can significantly improve performance.

Full disclosure: I am the developer of invocation_tree.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.