4

Here is an iterative example of a procedure computing the fibonacci sequence in SICP. The idea is:

  • a = fib(n+1) = a+b
  • b = fib(n) = a
(define (fib n) 
  (fib-iter 1 0 n))
(define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter (+ a b) a (- count 1))))

Looking at it deeper, I don't understand why continuing the computation towards fib(n+1) is necessary. I found we could have written.

;; a and b are first and second integers respectively
;; in the recursive call, b would be replaced by a+b because it is the next number in the sequence
;; so now a will be replaced by the previous value of b because it is the previous value.
(define (fib2 n) 
  (fib-iter 1 0 n))
(define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter b (+ a b) (- count 1))))

Now, I really think the first example, the one continuing up to n+1 is really redundant. I don't understand why is that necessary. What's wrong with my proposed iterative example?

lightning_missile
  • 2,821
  • 5
  • 30
  • 58
  • 3
    These are really just two equivalent ways of solving the exact same problem. I don’t think that the first version is “redundant” in any way: it’s doing the exact same amount of work as the second version. The difference here is trivial—I wouldn’t read into it or get worked up about it. I think your primary source of confusion may simply be that the original version uses `a` as the larger number and `b` as the smaller number, which is counterintuitive, and your second implementation reverses that ordering (which just so happens to work out due to how the algorithm works). – Alexis King Feb 28 '16 at 19:19
  • indeed the first version does have one "extra" member calculated, the (n+1)-th; but, the second also has one extra member - the (-1)-th _1_. :) To have the first stop earlier would complicate the code - while normally taking the `a` parameter as the result, for 0-th member we'd have to take the `b` as the result. – Will Ness Feb 29 '16 at 22:21

2 Answers2

1

There is nothing wrong. The two approaches give the same result.

#lang racket
(define (fib n) 
  (fib-iter 1 0 n))
(define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter (+ a b) a (- count 1))))

(define (fib2 n) 
  (fib-iter2 1 0 n))

(define (fib-iter2 a b count)
  (if (= count 0)
      b
      (fib-iter2 b (+ a b) (- count 1))))

(define xs '(0 1 2 3 4 5 6 7 8 9 10))
(map fib  xs)
(map fib2 xs)

The output is:

'(0 1 1 2 3 5 8 13 21 34 55)
'(0 1 1 2 3 5 8 13 21 34 55)

This shows that you are indeed computing the same sequence.

soegaard
  • 30,661
  • 4
  • 57
  • 106
1

Bot procedures produce correct result. However, the first one retains the relationship between a and b: a is Fib(i+1) and b is Fib(i) where i=n-count. The second method uses the first iteration to swap a and b around, thus introducing one redundant iteration. This can be seen by taking the trace of procedures:

> (define (fib n) 
  (fib-iter 1 0 n))

(define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter (+ a b) a (- count 1))))
> (trace fib-iter)
> (fib 3)
>(fib-iter 1 0 3)
>(fib-iter 1 1 2)
>(fib-iter 2 1 1)
>(fib-iter 3 2 0)
<2
2
> (define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter b (+ a b) (- count 1))))
> (trace fib-iter)
> (fib 3)
>(fib-iter 1 0 3)
>(fib-iter 0 1 2)
>(fib-iter 1 1 1)
>(fib-iter 1 2 0)
<2
2

What you actually want is something like this:

> (define (fib n)
  (if (zero? n)
      0
      (fib-iter 0 1 (- n 1))))

(define (fib-iter a b count)
  (if (zero? count)
      b
      (fib-iter b (+ a b) (- count 1))))

> (trace fib-iter)
> (fib 3)
>(fib-iter 0 1 2)
>(fib-iter 1 1 1)
>(fib-iter 1 2 0)
<2
2

Notice, there is one less iteration. However, I'm doing extra work in procedure fib.

mobiuseng
  • 2,326
  • 1
  • 16
  • 30
  • 1
    "a is Fib(i+1) and b is Fib(i)" - I get it. But why do they have to retain this relationship? I'm thinking the recursive process has this relationship though I can't see it yet. – lightning_missile Feb 29 '16 at 14:21
  • @morbidCode They don't. It just helps to reason about the process and correctness if the same property holds throughout. Your second version takes one iteration to swap `a` and `b` around in this relationship: not a bad idea actually (similarly to Euclid `GCD(m,n)` algorithm when `m – mobiuseng Feb 29 '16 at 16:23
  • so the lesson in this code is to try to retain property of code for better reasoning? Is this property the same as invariant? How important is retaining a relationship of variables? I'm asking because if I were to try to create an iterative process for fibonacci only being familiar with a recursive process, I don't think I would have came up with a solution the same as in the book. – lightning_missile Feb 29 '16 at 16:34
  • @morbidCode yes, that would be invariant. I think you would come up with something similar: you just need to find the way to keep last two values. BTW, SICP has a solution based on streams in Ch3, which is much more elegant. – mobiuseng Feb 29 '16 at 20:36
  • Ah, I think I figured it out! So the recursive process is like this, f(n-1)+f(n-2). But if we call f(n-1), it will do the same process again. But if we call f(n-1)+... inside f(n-1), the inner f(n-1) is equal to f(n-2) right? Then we just add them together. So actually in the recursive process, we only call f(n-2) so we can add the results of the two calls but in fact we can get f(n-2) from f(n-1)'s first inner call. So I think the variable a would be f(n-1), and b would be f(n-2). (cont) – lightning_missile Mar 01 '16 at 04:54
  • That's why a = a+b! Because we can get f(n-2) from f(n-1)! And the SICP iterative example retains this relationship as you said. My proposed example destroyed this relationship at the first iteration as you pointed out. (cont) – lightning_missile Mar 01 '16 at 04:55
  • 1
    so how a = 1 = fib(n+1) in the iterative process. It's not really obvious to me but I think the relationship should hold in both processes right? The only reason I can think of right now is: In the recursive process, fib(n) would always be fib(n-1) for the next number in the sequence. Since a = a+b, and b = a in the iterative process, b would be fib(n), and a (fib(n-1)) + b = fib(n+1)! – lightning_missile Mar 01 '16 at 05:35
  • did I understand all of it now? How much of what I said is actually correct? – lightning_missile Mar 01 '16 at 05:36
  • @morbidCode This sounds correct. Personally, I find it's easier to view the process going up in `n`: knowing `fib(n-1)` and `fib(n)`, I can compute `fib(n+1)=fib(n-1)+fib(n)`. Now, to start the process, in contrast to, for example, factorial, need 2 starting points: `fib(0)=0` and `fib(1)=1`. And the rest just follows. – mobiuseng Mar 01 '16 at 06:32
  • @morbidCode And it is probably easier to view the recursive process going down in `n`: to compute `fib(n+1)` need to sum up `fib(n)` and `fib(n-1)`. Should stop when `n<2` with `fib(n)=n`. – mobiuseng Mar 01 '16 at 06:34