フィボナッチ数列は大雑把に言えば 1 つ前の数値と 2 つ前の数値を足した結果を今の数値とする数列です。0 と 1 をスタートにして 0+1=2, 1+2=3, 2+3=5, 3+5=8,..といった具合です。漸化式で表現すると次です。
- F0 = 0,
- F1 = 1,
- Fn+2 = Fn + Fn+1 (n ≥ 0)
漸化式版
PHP ではこの漸化式を次の様に一行で書けます。
<?php // 21個分のフィボナッチ数列を $v に格納するワンライナー for([$v=[0,1],$i=1];count($v)<=21;$i++)$v[]=$v[$i-1]+$v[$i]; // ワンライナーの結果を echo echo implode(',', $v); // 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946
これを読みやすくなるようにインデントとかっこをつけると次です。
for ([$v = [0, 1], $i = 1]; count($v) <= 21; $i++) { $v[] = $v[$i - 1] + $v[$i]; }
この中にある下記の変数の初期化はワンライナー(というよりネスト制御構文+一文)で書くための小技です。
// for ([$v = [0, 1], $i = 1]; count($v) <= 21;) { の中にあります [$v = [0, 1], $i = 1]
PHP の変数の初期化は$i = 1;
の様に式の形で行います(詳細には整数定数 1 を変数 $i に代入する代入式)。
PHP: 式 – Manual
加えて、配列の定義において配列中の各要素は式で記述されます。これにより次の様に複数の代入式を要素とした配列を定義でき、一式で任意の異なる値を持つ変数を初期化できます。
PHP: 配列 – Manual
[ $v = [0, 1], // $v を初期化する代入式 $i = 1, // $i を初期化する代入式 ]
一般項版
フィボナッチ数列の一般項は次です。
これを用いると制御構文すら不要の一式でフィボナッチ数列を書けます。
echo implode(',',array_map(fn($n)=>(1/sqrt(5))*(pow((1+sqrt(5))/2, $n)-pow((1-sqrt(5))/2, $n)),range(0,21))); // 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946 // 本体は↓ // array_map(fn($n)=>(1/sqrt(5))*(pow((1+sqrt(5))/2, $n)-pow((1-sqrt(5))/2, $n)),range(0,21))
各 n を持つ range 関数を用意して array_map 関数で一般項を適用していくのみです。
PHP: range – Manual
PHP: array_map – Manual
コードの短縮
ちなみここで紹介したフィボナッチ数列ワンライナーをコードゴルフ的に短くすると次の様に50文字にできます。
for($v=[$i=0,1];end($v)<1e4;)$v[]=$v[$i]+$v[++$i];
基本は構文整理で、増えた小技は配列末尾の値を見る end 関数と指数表記で終点を示す部分です。なかなか短くなりましたがFibonacci の様な本物のコードゴルフでは標準出力込みで40文字を切っている猛者ばかりです。