0

I'm learning about memoization in recursive functions and stumbled upon a fibonacci-example on Youtube. I never saw the person run the code so perhaps he wrote it wrong.

When I copied the code and tried to run it, i first got an error since I declared memo without a range, so I simply set the range to the input + 1 (since I'm not using the 0-index) and thus solved that problem. But now I'm stuck at the wrong return value.

def fib(num, memo=[]):
    if num == 0:
        return 0
    elif num == 1 or num == 2:
        return 1
    elif memo[num] != None:
        return memo[num]
    else:
        memo[num] = fib(num-1, memo) + fib(num-2, memo)
        return memo[num]

uInput = int(input("Fibonacci: "))
memo = list(range(uInput + 1))
fibNum = fib(uInput, memo)

print(fibNum)

The code above throws no error but simply returns the "uInput" value. So if I input 6, for the 6st fibonacci number, the program returns 6 instead of 8, which is the actual 6st number. I am at a loss as to why this occurs.

When I have googled the issue I have found threads that suggested using dicts instead of lists. Thats all fine if that is the only way to do it. But if there is a way to make it work with a list I would like to understand how that is done so that I understand why I run in to this problem.

4 Answers 4

1

Accessing memo[num] will never return None. If num is out of range, a IndexError will be raised. Moreover, you typically do not want to pass a memo argument to your function and instead let it rely on its own memo object.

In your case, you want to check that the index is in range with len. Whenever num is out of range, the output must be computed recursively and memoized. Only then can it be returned.

def fib(num, memo=[0, 1]):
    if num >= len(memo):
        memo.append(fib(num - 1) +  fib(num - 2))
    return memo[num]

print(fib(10)) # 55

By the way, the above can easily be transformed into an iterative function which is typically more efficient in Python.

def fib(num, memo=[0, 1]):
    while num >= len(memo):
        memo.append(memo[-1] + memo[-2])
    return memo[num]
Sign up to request clarification or add additional context in comments.

2 Comments

I really like the idea with append, I find it trickier as it might seem on the first look. +1 for that.
This works, with the modification that you must, send memo with the recursive call as well. The function expects two arguments and will throw an error if both are not sent.
0

You filled the memo list with list(range(uInput + 1)). Your function then tests memo[num] != None, which has to be true. So it always does return memo[num], thus returning the number put in the memo list by the range function.

You should delete this line

memo = list(range(uInput + 1))

and only pass the first parameter when calling your function.

Comments

0

There is an error here:

memo = list(range(uInput + 1))  # wrong

The memo should contain None at index i for each result fib(i) not computed yet:

memo = [None] * (uInput + 1)  # right

The memo could be initialized to the beginning of the sequence 0,1 which will simplify the function:

def fib(num, memo):
    if memo[num] is None:
       memo[num] = fib(num-1, memo) + fib(num-2, memo)
    return memo[num]

uInput = int(input("Fibonacci: "))
memo = [0,1] + [None]*uInput
fibNum = fib(uInput, memo)

print(fibNum)

Update:

There is another mistake in the original code: memo is a required argument, it cannot work with the default in general.

Comments

0

I believe your question makes an excellent argument why memoization should be applied as a decorator instead of being intertwined with the function itself:

from functools import lru_cache

@lru_cache()
def fibonacci(number):
    if number == 0:
        return 0

    if number == 1 or number == 2:
        return 1

    return fibonacci(number - 1) + fibonacci(number - 2)

uInput = int(input("Fibonacci: "))

fibNum = fibonacci(uInput)

print(fibNum)

Otherwise you're trying to debug two different programs at the same time. Try the above with an input of 100 with, and without, the @lru_cache() decorator.

This, of course, is still limited to relatively small inputs by the depth of the Python call stack. There are iterative (and even better recursive) algorithms that can do better.

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.