フィボナッチ数列と黄金比
遅ればせながらダ・ヴィンチ・コードを読んでいます。映画は観てませんけど。(^_^)
まだ上巻の途中ですが、途中で登場したフィボナッチ数列のと黄金比の関係にビックリ。
ダ・ヴィンチ・コード〈上〉 p171-172 より引用
スライド映写の準備を進めながら、ラングドンは黄金比がフィボナッチ数列から導き出されることを説明した。その数列は、隣り合うふたつの項の和がつぎの項の値に等しいことで名高いが、隣り合うふたつの項の比がある数へ近づいていくという性質も持っている。その数こそ黄金比すなわち約1.618だ。
とあります。フィボナッチ数列も黄金比も知ってたのに、知りませんでした。(^_^;)
黄金比の正確な定義はですね。
Perl で実際に計算してみます。フィボナッチ数列を求める関数は前に作ったので流用します。怠惰であれ。
use strict;
my @fibmemo = ( );
sub fib {
my $n = shift;
if ($n <= 2) {
return 1;
} else {
if (!$fibmemo[$n]) { $fibmemo[$n] = fib($n-1) + fib($n-2) }
return $fibmemo[$n];
}
}
for my $i (1..100) {
printf "%-4d: %s\n", $i, fib($i+1) / fib($i);
}
fib がフィボナッチ数列の第N項を求める関数、@fibmemo は fib をメモ化して高速化する為の配列ですね。詳しくはフィボナッチ数列を参照してください。
とりあえず、100回計算してみます。計算結果は printf で書式出力しています。比を表示するのに「%f」や「%e」では精度が少なかったので「%s」にしました。
実行結果はこのようになりました。長くなるので一部中略しています。
1 : 1 2 : 2 3 : 1.5 4 : 1.66666666666667 5 : 1.6 6 : 1.625 7 : 1.61538461538462 8 : 1.61904761904762 9 : 1.61764705882353 10 : 1.61818181818182 (中略) 30 : 1.61803398875054 31 : 1.61803398874965 32 : 1.61803398874999 33 : 1.61803398874986 34 : 1.61803398874991 35 : 1.61803398874989 36 : 1.6180339887499 37 : 1.61803398874989 38 : 1.6180339887499 39 : 1.61803398874989 40 : 1.61803398874989 (中略・以降全て同じ) 95 : 1.61803398874989 96 : 1.61803398874989 97 : 1.61803398874989 98 : 1.61803398874989 99 : 1.61803398874989 100 : 1.61803398874989
約1.618 (黄金比)を上回ったり下回ったりして、確かに 1.618 に収束していくように見えますが、小数点の精度があまり良くないせいか 第39項以降は変化が見られなくなっています。
もう少し精度を上げるにはどうしたら良いんだろう……と、ラクダ本をぱらぱら眺めていたら Math::BigFloat モジュールの解説を見つけました。どうやらこれを使えばいいようです。
というわけで少し修正を加えてみました。
use strict;
use Math::BigFloat;
my @fibmemo = ( );
sub fib {
my $n = shift;
if ($n <= 2) {
return 1;
} else {
if (!$fibmemo[$n]) { $fibmemo[$n] = fib($n-1) + fib($n-2) }
return $fibmemo[$n];
}
}
for my $i (1..100) {
my $n = Math::BigFloat->new( fib($i) );
my $m = Math::BigFloat->new( fib($i+1) );
printf "%-4d: %s\n", $i, $m / $n;
}
Math::BigFloat クラスは演算子をオーバーロードしているので、普通の数値と同じように四則演算などが使えます。こんなに簡単で本当に精度が上がるのでしょうか。(^_^;)
実行結果は以下の通りです。やはり中略しています。
1 : 1
2 : 2
3 : 1.5
4 : 1.666666666666666666666666666666666666667
5 : 1.6
6 : 1.625
7 : 1.615384615384615384615384615384615384615
8 : 1.619047619047619047619047619047619047619
9 : 1.617647058823529411764705882352941176471
10 : 1.618181818181818181818181818181818181818
(中略)
30 : 1.618033988750540839382721984519975001202
31 : 1.618033988749648101530971893432887483854
32 : 1.618033988749989097047296779290725053241
33 : 1.618033988749858848350071980248415554997
34 : 1.618033988749908598925421457588060222831
35 : 1.618033988749889595896597819661196222364
36 : 1.618033988749896854407719255379913346986
37 : 1.618033988749894081903178586045254006188
38 : 1.618033988749895140905679158315141341105
39 : 1.618033988749894736402718110837895704559
40 : 1.618033988749894890909100680999418033989
(中略)
95 : 1.618033988749894121047505399789885179296
96 : 1.618033988749895125953876782198552998931
97 : 1.618033988749898329723193367763716000753
98 : 1.618033988749890562030085693389438977441
99 : 1.61803398874989648537756455709792665248
100 : 1.618033988749894222860154807156958477852
すごい桁数ですね。恐るべし BigFloat。(^_^;) 変化する位が段々と小さくなっていくのがわかります。
本当に近づいているのか調べる為に、黄金比の定義を BigFloat の精度で表現してみます。
use strict;
use Math::BigFloat;
my $phi = Math::BigFloat->new('5');
$phi->bsqrt(); # square-root
$phi->badd('1'); # plus one
$phi->bdiv('2'); # divided by two
print $phi;
黄金比を表すのに「φ」(ファイ)がよく使われるので、$phi としました。ダ・ヴィンチ・コードの作中でも黄金比の事を PHI と呼んでいます。
まず、「5」の BigFloat オブジェクトを作り、それに平方根をつけて、1を足し、2で割っています。今度は演算子ではなくメソッドを使ってみました。名前に「b」が付くメソッドはオブジェクト自身を変化させるので注意が必要です。
実行するとこのように表示されました。
1.61803398874989484820458683436563811772
さきほどのフィボナッチ数列の各項の比と比べると、どんどんこの値に近づいているのがよくわかりますね。(^_^) 念のために書いておきますが、この数値はあくまで BigFloat の精度による近似値です。黄金比は無理数なので、小数で表すと桁は無限に続くはずです。
Perl は数値演算もお手軽に出来て楽しいですね。