A linear algebra view of Fibonacci sequence
はじめに
フィボナッチ数列の高速な算法である
- 逐次平方変換
- Binet公式
について、線形代数的な視点から説明を与えて、ベンチマークしてみました。
なおどちらの方法についても、詳しい説明は世の中に出回っているはずですが、私にとってそれらの説明は天才のひらめきのような印象が拭えませんでした。そこで、あえて線形代数観点から説明を与えることで、「誰もが思いつきそうな、ありきたりの手法じゃないか」と感じて頂けたら嬉しいです。
なお、以下ではフィボナッチ数列の初項を F_0 = 0, F_1 = 1 としました。
逐次平方変換
フィボナッチ数列の漸化式を下記のように表現します:
初項と係数行列が等しいことに注意すれば、下記のようになります。
ここで行列の結合性をもちいて、冪乗を下記の擬似コードのように O(log N) で計算すると、いわゆる逐次平方変換と等価な式が得られます。
// O(log N) な冪乗計算 def pow(b: Matrix, e: Int): Long = if (e == 0) IdentityMatrix else (e % 2) match { case 0 => pow(b * b, e / 2) case 1 => b * pow(b, e - 1) } }
Binet 公式
フィボナッチ数列の一般項を示す Binet 公式を、線形代数の言葉で表現すれば、「対角化」の一言につきます。
漸化式を下記のように行列表記して、
そのまま一般項を係数行列の冪で表現し、
ここで、行列の冪を計算するに際して、対角化して対角成分の冪を計算すると Binet の公式が得られます。
説明的コード
どうも日本語の説明が下手くそなので、いままで述べた内容を scala コードで示します。