Fibonacci series and Dynamic programming
functionspace.org
functionspace.org
λ> let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
λ> let fib n = fibs!!n
λ> fib 100
354224848179261915075 lazy val fib:Stream[BigInt] = 0 #::1 #:: fib.zip(fib.tail).map(x=>x._1+x._2)
This is neat and all, but the imperative version wins by being O(1) in space. Right? (defun fib-help (a b curX desiredX)
(if (= curX desiredX)
b
(fib-help b (+ a b) (+ curX 1) desiredX)))
(defun fib (x) (fib-help 0 1 1 x))
(No, I don't really know lisp that well... learning.) let fibs = 1 : 1 : zipWith (+) fibs (tail fibs) in fibs !! 100
Since the fibs list is visible only to the expression (fibs !! 100), and since the (!!) operator advances through the list discarding elements until it hits the 100th, those elements are free to be garbage collected. Further, since the elements of the list (after the first two) are generated only when consumed, only a few of the cons cells will ever be live at any point in time.Specifically, I would expect space to grow at O(n), but that it could be garbage collected quickly. The question is, how quickly would the garbage collector do its thing. Or are you saying that is dodged in the Haskell version, as well?
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
we can arrive at the iterative (dynamic-programming) version that follows, def fib(n):
fibn1, fibn2 = (0, 1)
for _ in xrange(n):
fibn1, fibn2 = fibn2, fibn1 + fibn2
return fibn1
through the following steps: https://gist.github.com/tmoertel/5798134If you're interested in this kind of thing, I'm writing a series of articles on converting recursive algorithms into iterative algorithms. The initial article: http://blog.moertel.com/posts/2013-05-11-recursive-to-iterat...
Doh - I'm a moron! You're using a recurrence relation, which has the nice property of never computing the same sub-solution twice. I'll leave this here as a reminder to think before I nitpick next time.
Original:
Apologies to nitpick, but your iterative python solution isn't DP. DP hinges on the idea of never computing the answer to the same problem twice. For it to be DP, it would need to memoize subproblem solutions and refer to the solution cache rather than recompute the solution to every subproblem in the tree every time.
This solution fits the bill, although it can probably be written a bit more elegantly:
fibs = {0: 1, 1: 1}
def fib(n):
global fibs
stack = []
for a in xrange(n,0,-1):
if a in fibs:
stack.reverse()
for item in stack:
fibs[item] = fibs[a] + fibs[a-1]
a = item
break
else:
stack.append(a)
return fibs[n] def fib(n):
a, b = 0, 1
for k in range(n):
a, b = b, a + b
return a def fib_sicp(n):
a, b, p, q = 1, 0, 0, 1
while n > 0:
if n % 2 == 0: # even
oldp = p
p = p*p + q*q
q = 2*oldp*q + q*q
n //= 2
else:
olda = a
a = b*q + a*q + a*p
b = b*p + olda*q
n -= 1
return b
[1]: http://stackoverflow.com/questions/1525521/nth-fibonacci-num...It is ~20 times faster for n=10000
$ maxima
(%i3) load(eigen)$
(%i4) A: matrix([1, 1], [1, 0])$
(%i5) b: columnvector([1, 0])$
(%i6) fib(n) := (''A^^n . ''b)[2][1];
<n>
[ 1 1 ] [ 1 ]
(%o6) fib(n) := (([ ] . [ ]) )
[ 1 0 ] [ 0 ]
2
1
(%i7) makelist(fib(n), n, 10);
(%o7) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Since the transition matrix A is symmetric, it can be diagonalized using its eigenvector matrix: (%i8) similaritytransform(A)$
(%i9) Adiag: expand(leftmatrix . A . rightmatrix)$
And the n-th power of a diagonal matrix is the same as raising the values on its diagonal to the n-th power: (%i10) AdiagN(n) := matrix([''(Adiag[1][1])^n, 0], [0, ''(Adiag[2][2])^n]);
And thus raising A to the n-th power is the same as raising its diagonal counterpart Adiag to that power and then reversing the diagonalization transform (since leftmatrix and rightmatrix are inverses and cancel out for the interior multiplications): (%i11) fibdiag(n) := expand(rightmatrix . AdiagN(n) . leftmatrix . b)[2][1]$
Finally, this leads to a closed-form representation for the n-th Fibonacci number: (%i12) fibdiag(n);
sqrt(5) 1 n 1 sqrt(5) n
(------- + -) (- - -------)
2 2 2 2
(%o12) -------------- - --------------
sqrt(5) sqrt(5)Also, for this particular problem, see the "Topic: Linear Recurrences" chapter of Hefferon's book. It actually uses the Fibonacci series as its example (this example seems popular with authors of textbooks and lecture notes).
[1] http://ocw.mit.edu/courses/mathematics/18-06-linear-algebra-...
> my @fib := 1, 1, *+* ... *; @fib[99]
354224848179261915075
> (1, 1, *+* ... *)[99]
354224848179261915075
[1] https://justrakudoit.wordpress.com/2010/12/29/perl-6-fibonac... (def fib-list (map first (iterate (fn [[a b]] [b (+ a b)]) [0N 1N])))
(nth fib-list 100)
354224848179261915075N