フィボナッチのような単純な問題に対して再帰を使わないようにしてください。また、一度だけ使用する場合は、配列を使用してすべての結果を保存しないでください。 2つ前のフィボナッチ数を含む2つの要素の配列で十分です。各ステップで、これらの2つの数値を合計するだけです。 2つの連続したフィボナッチ数をどのように保存できますか?まあ、あなたは2つの連続する整数を持つときには1つが偶数であり、1つが奇数であることを知っています。そのフィーチャを使用してフィボナッチ数を取得/配置する場所を知ることができます。fib(i)
の場合、i
が偶数(i%2
が0)の場合は配列の最初の要素(index 0)に配置し、そうでなければ(i%2
は1) 2番目の要素(インデックス1)に配置します。なぜあなたはそれをそこに置くことができますか?あなたがfib(i)
を計算しているときに、fib(i)
にある値はfib(i-2)
になるはずです((i-2)%2
はi%2
と同じです)。しかし、fib(i-2)
はもう必要ありません。fib(i+1)
はまだfib(i-1)
(まだ配列に入っています)とfib(i)
(配列に挿入されたばかり)が必要です。
ですから、このようなfor loop
と再帰呼び出しを置き換えることができます:
int fibonacci(int n){
if(n <= 0){
return 0;
}
int previous[] = {0, 1}; // start with fib(0) and fib(1)
for(int i = 2; i <= n; ++i){
// modulo can be implemented with bit operations(much faster): i % 2 = i & 1
previous[i&1] += previous[(i-1)&1]; //shorter way to say: previous[i&1] = previous[i&1] + previous[(i-1)&1]
}
//Result is in previous[n&1]
return previous[n&1];
}
ための時間(関数呼び出し)のプログラミングとressources(スタック)は、それが消費しながら、再帰が実際にdiscommandedされます。だから、あなたが再帰を使うたびに、 "現在の位置"を保存するために必要ならば、単純なポップ/プッシュ操作でループとスタックに置き換えてみてください(C++ではvector
を使うことができます)。フィボナッチの場合、スタックは必要ありませんが、たとえばtree datastructureを反復処理する場合は、スタックが必要です(実装にもよりますが)。私の解決策を探していたとき、私は@naomikがwhile
ループの解決策を提供しているのを見ました。それも問題ありませんが、私はモジュロ演算(少し短く)で配列を優先します。
今度はサイズlong long int
の問題については、大きな数字(たとえば、GMP libraryまたはBoost.multiprecisionなど)の操作を実装する外部ライブラリを使用して解決できます。しかし、あなたはJavaからBigInteger
のようなクラスの独自のバージョンを作成し、私が持っているような基本的な操作を実装することもできます。私は私の例で追加したものだけを実装しました。他のものは実装してみましょう。
主なアイデアは簡単です。BigInt
は、little endianの表現を部分的に切り捨てることで大きな10進数を表します(最後にリトルエンディアンが使用される理由を説明します)。これらの部分の長さは、選択したベースによって異なります。 10進表記で作業したい場合は、基数が10の累乗である場合にのみ作用します。:基数として10を選択すると、それぞれが1桁を表します。100(= 10^2) (10^3)として1000を選択すると、各ピースは3桁の連続した数字を表すようになります(リトルエンディアンを参照)。ベースが100の場合、12765は[65, 27, 1]
、1789は[89, 17]
、505は[5, 5]
(= [05,5])、...ベース1000の場合:12765は[765, 12]
、1789は[789, 1]
、505 [505]
となります。それは最も効率的ではありませんが、それは最も直感的です(私は思う...)
さらには私たちが学校で学んだ紙に加えてのようなビットは次のようになります。
- 他の1
- の対応する部分とそれを追加し
BigInt
- の最低の作品で始まりますその合計の最下位ピース(=基底の和のモジュラス)が最終結果の対応する部分になります
- "大きな"ピースの合計は、次のピースの合計に加算されます(
- 手順2に進む例えば、次の部分
- ない片が残っていない場合、(それが左片を有する場合)、キャリーおよび他
BigInt
の残りの大きな部分を追加
:
9542 + 1097855 = [42, 95] + [55, 78, 09, 1]
lowest piece = 42 and 55 --> 42 + 55 = 97 = [97]
---> lowest piece of result = 97 (no carry, carry = 0)
2nd piece = 95 and 78 --> (95+78) + 0 = 173 = [73, 1]
---> 2nd piece of final result = 73
---> remaining: [1] = 1 = carry (will be added to sum of following pieces)
no piece left in first `BigInt`!
--> add carry ([1]) and remaining pieces from second `BigInt`([9, 1]) to final result
--> first additional piece: 9 + 1 = 10 = [10] (no carry)
--> second additional piece: 1 + 0 = 1 = [1] (no carry)
==> 9542 + 1 097 855 = [42, 95] + [55, 78, 09, 1] = [97, 73, 10, 1] = 1 107 397
Hereはどこデモあります私は10000のフィボナッチを計算するために上記のクラスを使用しました(結果は大きすぎてコピーできません)
幸運を祈ってください!
PS:なぜリトルエンディアンですか?実装の容易さのために、数字と繰り返しを追加するときはpush_back
を使用できますが、操作の実装は配列の最後の部分ではなく最初の部分から開始されます。
私がこれに似た問題に取り組んだのは、 "数値"クラスを作成することです。あなたのアプローチの最良の実装は、おそらく 'char'のベクトルであり、0-9を格納する各charにあります。最も効率的ではありませんが、最も簡単です。 – Jas
私はちょうど[GMP](https://gmplib.org/)のようなものを使用します – naomik
あなたが探している用語は "bignum"です。 –