Problem 2 ver. 2, 3, 4: logarithmic complexity
Find the sum of all the even-valued terms in the Fibonacci sequence which do not exceed four million
The last time we had the straightforward O(n) solution: building a sequence, filtering out even values and adding them. We can improve a bit, noticing that actually, every third member of the Fibonacci sequence is even. We don't check then for evennes, but just jump over three components each time. This version 2 (I don't publish it here) should be several times faster, but still is O(n) in performance.
We can also express a member of the Fibonacci sequence via the third and sixth members from behind:
and compute those values as the values of a new sequence:
. This version 3 is essentially the same as the previous one and again, I don't publish it here.
The drastic improvement is obtained using the expression
(I've added it and a proof to the wikipedia article, but they immediately reverted my changes as "unsourced" --- this is pathetic). Now the sum is obtained just computing one Fibonacci member, and this can be done with O(log n).
Indeed, we can compute a Fibonacci member exponentiating the appropriate matrix, and this exponentiation, just like usual one, can be done with O(log n). I prefer this solution over using the golden ratio exponentiation formula (again logarithmic complexity), because only integer-operations are involved. So, this is the version 4 of the solution.
-
(define (fibonacci-member-logarithmic n) (matrix-2d-a12 (^-2d fibonacci-matrix n)))
-
(define (fibonacci-sum-even n) (/ (- (fibonacci-member-logarithmic (+ n 2)) 1) 2))
I quickly outlined a class for 2D matrices and operations with it:
-
(define-record-type matrix-2d (fields a11 a12 a21 a22))
-
(define identity-matrix-2d (make-matrix-2d 1 0 0 1))
-
(define fibonacci-matrix (make-matrix-2d 1 1 1 0))
-
(define (*-2d A B) (let ([a11 (matrix-2d-a11 A)]
-
[a12 (matrix-2d-a12 A)]
-
[a21 (matrix-2d-a21 A)]
-
[a22 (matrix-2d-a22 A)]
-
[b11 (matrix-2d-a11 B)]
-
[b12 (matrix-2d-a12 B)]
-
[b21 (matrix-2d-a21 B)]
-
[b22 (matrix-2d-a22 B)])
-
(make-matrix-2d (+ (* a11 b11) (* a12 b21)) (+ (* a11 b12) (* a12 b22))
-
(+ (* a21 b11) (* a22 b21)) (+ (* a21 b12) (* a22 b22)))))
-
(define (^-2d-linear A n) (apply-n-times identity-matrix-2d n (lambda (x) (*-2d x A))))
-
(define (^-2d-logarithmic A n) (if (= n 0) identity-matrix-2d
-
(if (odd? n) (*-2d A (^-2d-logarithmic A (- n 1)))
-
(let ([B (^-2d-logarithmic A (div n 2))]) (*-2d B B)))))
-
(define ^-2d ^-2d-logarithmic)
The solution is O(1) in memory and O(log n) in performance - of course, where n denotes the index of a number in the Fibonacci sequence. And we have been questioned about the cutset, where members of a sequence are less than certain number. Then an additional function (closest-fibonacci-index) comes in handy (see the wiki for explanation):
-
(define golden-ratio (/ (+ 1 (sqrt 5)) 2))
-
(define (closest-fibonacci-index f) (round (log (* f (sqrt 5)) golden-ratio)))
-
(define (solution-2-optimized-3 n) (fibonacci-sum-even (closest-fibonacci-index n)))
The final touch is asking ourselves about complexity of the (log) function. Well, it can be computed fast enough not to spoil complexity of the algo's main part.


















