フィボナッチ数列については誰もがよく知っています。
f(n) = f(n-1) + f(n-2)
f(0) = 1
f(1) = 1
この機能をコンピュータプログラムで実現する場合、上記の漸化式に従って再帰を利用することが容易に考えられますが、再帰を利用する利点は、プログラマが意図的に行う限り、具体的な計算プロセスを考慮する必要がないことです。再帰の明確な出口を見つけます。
int fib(int n)
{
if (n == 0 || n == 1) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
再帰の欠点は、関数呼び出しのオーバーヘッドが発生し、実行効率が比較的低いことです。
もちろん、問題を解決するために反復法を使用することもできます。反復は、既知の問題の解決策から開始され、未知の問題の解決策を継続的に導き出します。簡単に言うと、再帰はトップダウンのプロセスであり、反復はボトムアッププロセスです。
int fib(int n)
{
int temp1 = 1;
int temp2 = 1;
for (int i = 2; i < n; i++) {
int temp = temp1 + temp2;
temp1 = temp2;
temp2 = temp;
}
return temp1 + temp2;
}
反復法は自分自身を呼び出す処理がないため、再帰法に比べて効率が非常に高くなります。
再帰の実装を振り返ってみましょう fib(5) を解きたい場合は fib(4) と fib(3) を取得する必要があります fib(4) を解くには fib(3) と fib を取得する必要があります(2). fib( 3) を解くには fib(2) と fib(1) を取得する必要があり、fib(2) を解くには fib(1) と fib(0) を取得する必要があります。fib(3) を解くにもこのようなプロセスを経る必要があり、必然的に計算が繰り返され、関数のスタックとポップが繰り返されます。計算中に解決された結果を保存し、それを計算に直接使用すると、はるかに効率的になります。この方法は動的プログラミングと呼ばれます。
int fib(int n, int *table)
{
if (table[n]) {
return table[n];
}
table[n] = fib(n - 1) + fib(n - 2);
return table[n];
}
int get_value(int n)
{
int *table = (int *)malloc(sizeof(int) * (n + 1));
table[0] = 1;
table[1] = 1;
return fib(n, table);
}