フィボナッチをBinetで計算するときの計算量と精度

フィボナッチ数列Binetの公式浮動小数点指数計算数値解析アルゴリズム
フィボナッチ数列の第n項を求めるプログラムって、素朴に再帰で実装するとO(2^n)でヤバいけど、メモ化再帰やDPを使うとO(n)になるよね?でも一般項を初めから書いておけばO(1)じゃね?計算精度的な問題があるのかな?
21:45
リュウツー先生
リュウツー先生
おっしゃるとおり、Binetの公式を使えば「一見」O(1)O(1)で計算できそうですね。でも実際にはいくつか検討すべきポイントがあります。
まず、あなたが特にどこを気にしているのか教えてもらえる?

1. 「計算精度(丸め誤差)の問題」について詳しく知りたいのか?
2. 定数時間といっても5\sqrt5φnφ^nのべき乗計算には実はコストがある話を知りたいのか?
3. それとも「nがどのくらい大きい場合まで正しく整数として得られるか」を気にしている?
4. その他、何か具体的な利用シーン(言語や用途)がある?

どれについて掘り下げると分かりやすいかな?
21:45
2と3
15:33
リュウツー先生
リュウツー先生
いいね!!2と3を深掘りするってことで、まずは君がどれくらい分かってるか確認するよ。現状理解度確認フェーズだから、以下のクイズに答えてくれる?

問題1(計算量の観点)
φ = (1+√5)/2 のべき乗 φ^n を計算するとき、次のうち計算量(乗算回数・時間コスト)が最も現実的に見積もれるのはどれ?
A: O(n) — そのまま掛け算をn回やる
B: O(log n) — 繰り返し二乗法(binary exponentiation)を使う
C: O(1) — 組み込みの関数を呼べば定数時間で得られる

問題2(数値精度の観点)
Binetの公式を IEEE754 倍精度(double)で評価して四捨五入し整数を得る方式について、どれが正しい?
A: 任意の n で常に正しい(四捨五入すれば真のF(n)が得られる)
B: ある上限 n までなら正しく、それ以上は浮動小数点の有限精度(仮数桁数)で誤る可能性が出る
C: どの n でも誤差が大きくて実用にならない

どの選択肢を選ぶ?(1問でもOK)
15:34
1: C
2: B
15:36
リュウツー先生
リュウツー先生
いいね!!素晴らしい!!選択の1と2について整理して答えるね。

まず計算量(問題2):理論的な算術モデルを明確にしないと話が分かれる。固定幅の浮動小数点(たとえばIEEE double)を「基本演算は定数時間」と見るなら、ライブラリの pow や exp を呼べば実行時間は定数(実用的にはO(1))と扱える。ただし
- 高精度(任意精度)で正確に φn\varphi^n を求めるなら乗算回数は繰り返し二乗法で O(logn)O(\log n)、かつ各乗算は大きさに応じたコストがかかる(すなわち総コストは乗算アルゴリズムに依存する)。
要するに「実機のdoubleで速く出せる=O(1)扱いできるが、正確な大きなnnを目指すならコストは増える(少なくともO(logn)O(\log n)のアルゴリズムを使うのが普通)」ってこと。

次に精度(問題3):Binetの公式
Fn=φn(1φ)n5,φ=1+52F_n=\frac{\varphi^n-(1-\varphi)^n}{\sqrt5},\qquad \varphi=\frac{1+\sqrt5}{2}
をdoubleで評価して四捨五入する方法は「ある上限まで」正しい、つまり選択肢Bが正解。理由は機械精度(相対誤差約ε2.2×1016\varepsilon\approx2.2\times10^{-16})が乗算で増幅され、真の値からの絶対誤差が<0.5<0.5でないと丸めて正しい整数にならないから。概算するとdoubleではだいたい n7075n\lesssim 70\text{〜}75 程度まで安全(整数表現の上限 2532^{53} の関係で F78F_{78} あたりが境界でもある)。実務的には 「nが小さい(数十以下)」ならBinet+doubleで速く済ませて OK、より大きい n が必要なら整数での高速二分法(fast doubling)を使って O(logn)O(\log n) 回の大整数乗算で正確に求めるのが常套手段。

ここまではいい?続きをやるか、具体的な実装例(double版 vs fast-doubling bigint)見たい?
15:36
0 / 2000
マイセオくん