フィボナッチ数列の n 桁目を見つけるための対数時間手順を定義する演習用に読んだソリューションを理解しようとしています。問題は、コンピュータ プログラムの構造と解釈 (SICP) の 1.19 です。
ネタバレ注意:この問題の解決策については、以下で説明します。
Fib(n) は次のように線形時間で計算できます。 a = 1 および b = 0 から開始します。Fib(n) は常に b の値に等しくなります。したがって、最初は n = 0 で、Fib(0) = 0 です。次の変換が適用されるたびに、n は 1 ずつインクリメントされ、Fib(n) は b の値に等しくなります。
a <-- a + b
b <-- a
これを対数時間で行うために、問題の説明では変換 T を次のように定義します。
a' <-- bq + aq + ap
b' <-- bp + aq
ここで、最初は p = 0 および q = 1 であるため、この変換は上記のものと同じです。
次に、上記の変換を 2 回適用すると、新しい値 a'' と b'' を a と b の元の値で表すことができます。
a'' <-- b'q + a'q + a'p = (2pq + q^2)b + (2pq + q^2)a + (p^2 + q^2)a
b' <-- b'p + a'q = (p^2 + q^2)b + (2pq + q^2)a
次に、演習では、変換を 2 回適用するこのようなアプリケーションを「変換の 2 乗」と呼びます。私の理解は正しいですか?
この演習の解法は、上記の二乗変換の値を使用する手法を適用して、対数時間で実行される解を生成します。問題は対数時間でどのように実行されますか? 二乗変換を適用した結果を使用するたびに、2 回ではなく 1 回の変換を行う必要があるように思えます。では、どのようにしてステップ数を毎回半分に減らしていくのでしょうか?
schemewiki.org からの解決策は以下に掲載されています。
(define (fib n)
(fib-iter 1 0 0 1 n))
(define (fib-iter a b p q count)
(cond ((= count 0) b)
((even? count)
(fib-iter a
b
(+ (square p) (square q))
(+ (* 2 p q) (square q))
(/ count 2)))
(else (fib-iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- count 1)))))
(define (square x) (* x x))