How many additions are performed when we compute the $n^{th} $ Fibonacci number using the definition of fibs based on the add-streams procedure? Show that the number of additions would be exponentially greater if we had implemented (delay ⟨exp⟩) simply as (lambda () ⟨exp⟩), without using the optimization provided by the memo-proc procedure.
(define fibs
(cons-stream
0
(cons-stream 1 (add-streams (stream-cdr fibs) fibs))))
(define (memo-proc proc)
(let ((already-run? false) (result false))
(lambda ()
(if (not already-run?)
(begin (set! result (proc))
(set! already-run? true)
result)
result))))
(define (add-streams s1 s2) (stream-map + s1 s2))
He who forget
on the first sight, if you simply draw a graph like below (like I do), this question would be very confusing. shouldn’t It be like this? isn’t the number of addition is always $n-2$ whether we apply memo-proc ?
fibs: 0 1 1 2 ...
cdr fibs: 1 1 2 3 ...
+ + + +
fibs: 0 1 1 2 3 5 ...
well, for the evaluation result, it’s right, but wrong when it comes to how it is evaluated.
let’s expand it a little bit.
fibs = [0, 1, add fibs fibs.next] ;; for simplcity, rewrite fibs
;; expand till the third value can be evaluated
fibs = [0, 1, add [0, 1, add fibs fibs.next] [1, add fibs fibs.next]]
;; progress the otter 'add' of the third item (r shift for one item)
fibs = [0, 1, 1, add [1, add fibs fibs.next] [add fibs fibs.next]]
wow, the third value comes out as 1. and it’s obvious that it takes one add operation because we change one add. keep up for the forth value.
;; add fibs fibs.next <=> 1, add [1, add fibs fibs.next] [add fibs fibs.next]
;; above transformation equivalent and takes one add operation
fibs = [0, 1, 1, add [1, add fibs fibs.next] [1, add [1, add fibs fibs.next] [add fibs fibs.next]]]
fibs = [0, 1, 1, 2, add [add fibs fibs.next] [add [1, add fibs fibs.next] [add fibs fibs.next]]]
;; make a variable
f+ = [add fibs fibs.next]
fibs = [0, 1, 1, 2, add f+ [add [1, f+] [f+]]]
;; the first item in f+ is 1, and it is evaluated for two times
;; which brings two add operation
;; fibs = [0, 1, 1, 2, add f+ [add [1, f+] [f+]]]
;; ^ ^
;; plus two add should be performed to make the value move to otter space
;; fibs(4) = 3, A(4) = 2+2 = 4
;; so on and so forth
see, we not ‘remember’ each item, instead, each item is decomposed till reach fibs[0] and fibs[1].
for a general case w/o memo-proc, let $A(n)$ be the number of addition for the $n^{th}$ fibs, there is,
$$ A(n) = A(n-1) + A(n-2)+1,\ where~A(0) = 0,\ A(1)=0 $$
$A(2) = A(0) + A(1)+1 = 1$
$A(3) = A(1) + A(2)+1 = 2$
$A(4) = A(2) + A(3)+1 = 4$
$…$
and it will end up result to, in which k is the exponential base.
$$ k^{n-2}>\phi^{n-2},\ where\ \phi=\frac{\sqrt{5}+1}{2} $$
He who memo
the memo version does exactly the one at the very beginning
fibs: 0 1 1 2 ...
cdr fibs: 1 1 2 3 ...
+ + + +
fibs: 0 1 1 2 3 5 ...
however, the question is, how the hack does scheme remember it? where does it preserve the value?