1年以上前、「php-programmer」が空席に対応し、TKを送信し、フィボナッチからのタスクがありました: 1から10000の範囲のすべての偶数フィボナッチ数を選択します 。 (for)ループを使用して決定。 そこでは、ユーザーの最も近い誕生日のサンプルでSQLクエリを作成し、何かをする必要がありました。私は覚えていませんし、何らかの関数を書いています。 私はすべてをやりました。 彼らは答えを送りました:「あなたは受け入れられないテストタスクの結果によると。」 彼らがまったく好きではなかったものは書かれていませんでした。 たぶんフィボナッチが飛んだために、おそらく私は座って考えています... :)この投稿では、この問題を効果的に、おそらくは効率的に解決する方法を示しますが、これは正確ではありません。 同時に、フィボナッチ数について証明された数千の事実を実証します。
理論化
開始する最良の方法は、最初のN個のフィボナッチ数の目を見て、偶数の配列のパターンを見つけようとすることです。
すべての3番目のフィボナッチ数が偶数であり、おそらくすべての偶数が3の倍数の位置にあることがわかるように、偶数がシーケンスにマークされています。これは推測であるため、これを確認して計算アルゴリズムを計算する必要があります。
最良の証明は単純なので、最初に見逃していた単純なアイデアに感謝します。 後続の各フィボナッチ数は前の2つの合計で、前の2つの数が奇数の場合、次は偶数になり、前の2つの数で一方が奇数でもう一方が偶数の場合、次は奇数になります。 原理的には、このアイデアだけですでに実証済みの事実を「直感的に模索」するのに十分です。 帰納法による証明は明らかですが、私はそれをもたらさないように抵抗することはできません
「3番目のフィボナッチ数はすべて偶数であり、そのような各数に先行する2つは奇数であること」を証明します。
- ベース誘導 。 最初の2つのフィボナッチ数 -奇数、3番目の数字 -さえ
- 仮説 。 3のフィボナッチ数倍まで、3分の1が偶数で、その前の2つが奇数であると仮定します。
- 誘導ステップ 。 仮説の最後の偶数に続く数は奇数です、なぜなら 奇数と偶数の合計から取得されます。その数の後の次も奇数です。 偶数と奇数の合計から取得され、偶数の次は偶数であるため、 彼のために受け取った2つの前のものは奇妙であり、彼の数は3の倍数であり、それは偶数であり、前の2つは奇数です。
これは、数学的な計算に頼ることなく推測を証明する方法です。 このアイデアを形式化すると同時に、3つごとのフィボナッチ数を計算するための式を取得できます。 から そして 。 再帰関係を使用する 私達は得る:
だから -それでも また、この式のおかげで。 それを考えると 、フィボナッチ数の3分の1ごとが本当に偶数であり、推測の一部を裏付けています。 ここでは、他の偶数を見逃さないようにする必要があります。 それらはすべて3の倍数であることがわかりました。彼の単純なアイデアに対するjanatemに感謝します。 また、次の場合 -奇数 また、奇数なので、数字でその数字を取得します -奇数(誘導により、で始まる -奇数)、および -偶数、すべてのフィボナッチ数をカバーします。つまり、すべての偶数をカバーします。
他の偶数がないことを示すことができるため、別の方法があります。 番号が存在すると仮定します -そして それから または どこで -ある種の自然。
元の投稿からのフィボナッチ数の行列表現を見てみましょう
両方の部分の行列式を計算すると、
その結果、数の約数 そして マッチディバイダー 、つまり 隣接するフィボナッチ数は相互に単純です。 これはまた、相互の単純さと数字 そして 、つまり そして 。 しかし、仮定によって -偶数、そして -以前に証明されたように。 それ以外の偶数 どこで フィボナッチ数列には存在しません。 また、隣接するフィボナッチ数が相互に単純であるという興味深い事実を確立しました。
最後に、推測を証明するための少なくとももう1つの方法、ルークの定理を使用する方法を示します。
ルークの定理 。 整数除算 、そして 、それが除数である場合にのみ どこで 特に
ここに 最大の共通要因です。 入れたら それから 。 もし -さらに、左側が2であるため、右側も2に等しくなるようにする必要があります。 3で除算すると同時に、逆も同様です。 したがって、証明したかったものを正確に取得します。
そのため、フィボナッチ数の3分の1は偶数であり、1でも3の倍数です。 私たちはこれを慎重に証明しましたが、誰もそれを疑う勇気はありません。
アルゴリズム
そして今、アルゴリズムを思い付くことが残っています。 あなたは確かにpavellyzhinが元々やったことを行うことができますが、チェックする代わりに チェックできます 、これはひねりです! 確かに、シーケンスフィルターを変更しただけなので、これはアルゴリズムの反復回数には影響しません。 必要なプロパティ(パリティ)を使用して、フィボナッチ数のサブシーケンスをすぐに生成することをお勧めします。したがって、もう1つの明らかな方法は、Binet式を使用することです
計算の効率には問題があります。これについては、元の記事で詳しく説明しています。 したがって、私の意見では、最良の方法-反復計算を提案します 、これは以前に示したように、式 。 アルゴリズムで反復遷移を構築するには、計算する必要があります 、すべてが同じくらい簡単です
ところで、一般的に言えば、それを証明するのは簡単です
正式には、アルゴリズムは次のように記述されます(現在の偶数フィボナッチ数 続いてフィボナッチ数 、 問題の状態で与えられた数値の上限です):
- もし 、それから終了、そうでなければ結果に追加
- ステップ2に進みます。
アルゴリズムは非常に簡単です-フィボナッチ数の3つごとにジャンプして印刷します(それ以上でない場合) ) アルゴリズムの複雑さは依然として線形ですが、ステップ2の繰り返し回数は、範囲内の偶数フィボナッチ数に正確に等しくなります。 、比較のために、フィルタリングを使用した単純なアルゴリズムでは、3倍の反復が必要です(見つかったそれぞれに対して、2回の破棄があります)。
アルゴリズムは紙の上にありますが、インタビューは何でしたか、PHP? さて、最後にPHPの意味を明らかに
function evenfibs($ubound) { $result = []; [$evenf, $nextf] = [2, 3]; while($evenf <= $ubound) { array_push($result, $evenf); [$nextf, $evenf] = [ 3 * $nextf + 2 * $evenf, 2 * $nextf + $evenf]; } return $result; }
注 :たとえば、ユーザーhunrollが示したように、この方法はいつでも改善できます。 アイデアは、計算のために、部分的に得られた結果を除いて、余分なものを必要としないということです。 前の偶数フィボナッチ数のみを使用して偶数を計算できます。
function evenfibs($ubound) { if($ubound < 2) return []; $evens = [2]; $next = 8; for($i = 1; $next <= $ubound; $i++) { $evens[$i] = $next; $next = $evens[$i]*4 + $evens[$i-1]; } return $evens; }
一般化
ここでルークの定理に言及しました。 。 その結果、フィボナッチ数を取得できます の倍数 、その番号の場合のみ 倍数 。 つまり 4番目ごとのフィボナッチ数が3で除算され、5番目ごとに5ごと、6番目ごとに8ごとなどです。
次に、簡単な方法で、フィボナッチサブシーケンスを計算するアルゴリズムを取得します。このアルゴリズムでは、要素は特定の数の倍数です 。 という事実を使用して
前のアルゴリズムは
- もし 、それから終了、そうでなければ結果に追加
- ステップ2に進みます。
どこで 定数として設定できます。
メモの概要 。 ユーザーの無知が正しく指摘されているように、一般化されたケースでは、部分的な結果からの数字のみを使用するアルゴリズムを構築することも可能です。
t = 1およびt = 2の場合、数学的な帰納法によってこれを証明しましょう。 あるtまで保持すると仮定すると、
合計のようなもの
もちろん、タスクは完全に合成であり、反復回数は非常に少ないです( 答えには6つの数字しか含まれていません。 6回の反復が完了し、元の「正面」アルゴリズムには18)が必要でしたが、この方法で、ここで新しい何かを発見したユーザーは、フィボナッチ数のインタビューで少し深い知識を示すことができます。
編集: janatemとAllexInのユーザーの簡単な証明のおかげで、私はそれらを「理論化」に加え、計算で既に得られた偶数のみを使用するアルゴリズムの探索と、それを一般化するためのユーザーの無知に参加しました。