C++で合計がKになるフィボナッチ数の最小個数を求める方法
数値 k が与えられたとき、合計がちょうど k と等しくなるようなフィボナッチ数の最小個数を求めます。ただし、同じフィボナッチ数は複数回使用しても構いません。
例えば、入力が k = 7 の場合、出力は 2 になります。フィボナッチ数列は 1, 1, 2, 3, 5, 8, 13, ... と続きますが、k = 7 の場合は 2 + 5 = 7 という 2 つの数の組み合わせで表現できるためです。
アルゴリズム(貪欲法)
この問題は貪欲法(グリーディ法)を用いることで効率的に解けます。基本的な考え方は、「k を超えない最大のフィボナッチ数を選び、残りの値に対して同じ操作を繰り返す」というものです。ゼッケンドルフの定理(Zeckendorf's theorem)によれば、「すべての正整数は互いに連続しないフィボナッチ数の和として一意に表現できる」ことが証明されているため、この貪欲な選択が常に最適解を導きます。
具体的には、以下の手順に従います。
- 配列
fを定義する fの末尾に 0 を挿入するfの末尾に 1 を挿入するfの最後の要素がk以下である間、次を繰り返す:- (
fの最後の要素 + 最後から 2 番目の要素)をfに挿入する
- (
ret := 0と初期化するj := fの最後のインデックス とする- (
j >= 0かつk > 0)である間、次を繰り返す:- もし
f[j] <= kならば:k := k - f[j]retを 1 増やす
- そうでなければ:
jを 1 減らす
- もし
retを返す
C++ 実装例
以下の実装例を見ると、より理解が深まるでしょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int findMinFibonacciNumbers(int k) {
vector<int> f;
f.push_back(0);
f.push_back(1);
while (f.back() <= k) {
f.push_back(f[f.size() - 1] + f[f.size() - 2]);
}
int ret = 0;
int j = f.size() - 1;
while (j >= 0 && k > 0) {
if (f[j] <= k) {
k -= f[j];
ret++;
}
else
j--;
}
return ret;
}
};
main(){
Solution ob;
cout << (ob.findMinFibonacciNumbers(7));
}
入力
7
出力
2
計算量について
フィボナッチ数列の生成は O(log k) で完了し、貪欲な選択処理も同様に O(log k) で終了するため、全体の時間計算量は O(log k) となり、非常に効率的です。空間計算量も、生成したフィボナッチ数列を格納する分の O(log k) に抑えられます。
-
C++でXとの合計がフィボナッチ数になるノードを数える方法
各ノードに数値の重みが割り当てられた二分木が与えられます。この記事の目的は、「ノードの重み + X」の計算結果がフィボナッチ数となるノードの個数を求めることです。フィボナッチ数列とは、0, 1, 1, 2, 3, 5, 8, 13… のように続く数列で、n番目の数は(n−1)番目と(n−2)番目の数の和になります。たとえば重みが13であればフィボナッチ数に該当するため、そのノードはカウント対象となります。入力例1temp = 1 の場合。値を入力すると、以下のような木が構成されます。出力Count the nodes whose sum with X is a Fibonacci number
-
C++でフィボナッチ数列の2乗の総和を求める方法
フィボナッチ数列とはフィボナッチ数列とは、0から始まり「直前の2つの数の和が次の数になる」という規則に従う数学的な数列です。たとえば、最初の数が0、2番目の数が1であれば、その和である1が3番目の数となります。F0=0, F1=1これを漸化式で表すと次のようになります。Fn = Fn-1 + Fn-2F2 = F0 + F1 = 0 + 1 = 1さらに、1と1を足すと次の数は2になります。F1=1, F2=1F3 = F1 + F2 = 1 + 1 = 2したがって、フィボナッチ数列は以下のように続いていきます。0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …フィボナッチ