18

How do I implement a recursive Fibonacci function with no loops running in O(n)?

munk
  • 12,340
  • 8
  • 51
  • 71
Mat.S
  • 1,814
  • 7
  • 24
  • 36

4 Answers4

56

Typically I'd be against posting an answer to a homework question like this, but everything posted so far seems to be overcomplicating things. As said in the comments above, you should just use recursion to solve the problem like you would do iteratively.

Here's the iterative solution:

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

Here's an equivalent recursive solution:

def fib(n):
  def fib_help(a, b, n):
    return fib_help(b, a+b, n-1) if n > 0 else a
  return fib_help(0, 1, n)

Note that in both cases we actually compute up to Fn+1, but return Fn as the result. This fits nicely with the "hint" you were given.

I hope that you'll take the time to compare the two solutions and convince yourself that they're equivalent. Understanding how to transform an iterative solution to an equivalent recursive one (or vice versa) is a good skill to develop.

DaoWen
  • 32,589
  • 6
  • 74
  • 101
  • Tail recursive form, eh? This is another good way to do it. Very useful skill in Scheme or other functional languages, and good to know just for the perspective it gives you. Not always the most practical transformation in Python, but neither is the solution the hint intends. – user2357112 Mar 01 '14 at 07:34
  • 10
    No need for fib_help if you give a and b to fib as default parameters: `def fib(n, a=0, b=1):` and `return fib(n-1, b, a+b) if n > 0 else a` – gens Jul 20 '16 at 21:52
1

Scala code for finding nth Fibonacci Number. For more information on Tail Recursion http://nerds.logdown.com/posts/1406258-n-th-fibonacci-number

object Main {
     def main(args: Array[String]) {
        println(fib(9));
        println(fib(8));
        println(fib(7));
        println(fib(6));
        println(fib(5));
        println(fib(4));
        println(fib(3));
        println(fib(2));
        println(fib(1));
        println(fib(0));
      }
      def fib(n: Int): Int = {
        def fib(n: Int, p :Int, c: Int): Int ={
          if (n == 0) return -1; // undefined
          if (n == 1) return p;
          fib(n-1, c, p + c)
        }
        fib(n, 0, 1);
      }
    }
Deepak Kumar
  • 161
  • 1
  • 6
1

In case someone is looking for a JavaScript solution:

function _fib(n, left, right) {
  switch (n) {
    case 0: return 0
    case 1: return right
    default: return _fib(n - 1, right, left + right)
  }
}

function fib(n) {
  return _fib(n, 0, 1)
}

That runs in O(n) time and O(1) space with tail call optimization.

bcherny
  • 3,072
  • 2
  • 27
  • 35
0

To solve this in linear time, you must use a dynamic programming technique known as memoization.

The algorithm for Fibonacci in pseudo-code, using memoization, looks like this:

memoryMap[n]

func fib(int n)
    if (n is in memoryMap) then
        return memoryMap[n]
    if (n <= 1) then
        memoryMap[n] = n
    else
        memoryMap[n] = fib(n-1) + fib(n-2)

    return memoryMap[n]

To explain, you after each call to fib(x) you be storing the result in a memory map. For each subsequent call, all lookups to fib(x) will be free: that is, looking up the result in the memory costs only O(1) time.

831
  • 27
  • 2
  • 1
    This is a way to do it, and the first way that came to mind for me too, but not the intended solution. How much of a good or bad thing that is is a matter of opinion, but if the OP uses this, I would recommend the OP also figure out and submit the intended solution. – user2357112 Mar 01 '14 at 07:26
  • 1
    Reading it again it was obvious the correct solution was a tail-recursion. But then again, it still doesn't make much sense, as Python doesn't even support tail-recursion elimination. – 831 Mar 01 '14 at 08:10
  • It does make sense even without tail recursion as that it only uses `n` recursive calls which should fit onto the stack for `fib`. Compare that to the naive recursion `fib(n) = fib(n-1) + fib(n-2) if n >1 else n` – kap Mar 22 '17 at 20:52
  • 1
    I feel this _memoization_ is a natural thing to do, in order to avoid computing things multiple times, you save those results of computations somewhere, where you can look up the results in the future. It seems to me that calling it "dynamic programming" adds a lot of steam to something very simple. A version without using mutation or assignments would be interesting. – Zelphir Kaltstahl Jul 16 '17 at 12:10