目次
動的計画法の禅
動的計画法は、アルゴリズムの基本部分の中で最も興味深い部分です. 私は何日も前から動的計画法をいくつかの短い言葉で要約する方法を考えてきました. 前回のアルゴリズムの要約のように.この知識を完全に取り入れてください。
すると、貪欲な戦略と同じように、動的計画法も決まった決まりごとではなく、「過去の蓄積を利用して現在の問題を解決する」という考え方であることがわかり、私はこれを動的計画法の禅と呼んでいます。
さまざまな緯度でフィボナッチ数列を解く
フィボナッチ数列とは
フィボナッチは、一連の数値で一般化できます。
1,1,2,3,5,8,....,num[n-1],num[n],num[n-1]+num[n]
以下はその再帰構造です。つまり、num[n] = num[n-1] + num[n-2] if n >= 2
素朴な再帰スキーム
このルールを使用して、非常に簡単な方法で N 番目の値のサイズを計算するプログラムを作成できます。
int fib(int n) {
if(n<=0) return 0;
if(n<=2) return 1;
return fib(n-1) +fib(n-2);
}
このプログラムの実行構造は次のとおりです。
サイズ N の問題をサイズ N-1 と N-2 の 2 つの問題に分解し、fib(1)、fib(2) に落ちるなど、問題が解決済みの問題になるまで分割を続けます。
単純再帰問題
実際にこのコードを実行してみると、求める値が大きくなると、計算にかかる時間が非常に長くなることがわかります。これは時間の複雑さに関係しています. 上のツリーを見るのは難しくありません. 各ノードが一度計算したいことを表している場合, 2^(n -1) 回計算する必要があります.
この木の問題点を調べる必要があります. 繰り返し計算が多すぎることは明らかです. fib(x) が計算されるたびに, その値を fib(1) と fib(2) までさかのぼる必要があります.前に計算された値を直接使用する方法はありますか? 再計算するのではなく。
FIB トップダウン、覚書スキーム
今回はアルゴリズム開始前にサイズ N の領域を申請します. この領域は計算値を記録するために使用されます. 後で再度使用するときは, キャッシュ領域の値が再計算されずに直接使用されます.
キャッシュは私たちが普段使っているメモとどこか似ていて、計算もトップダウン式なので、この方式はトップダウン方式やメモ方式とも呼ばれます。
コードは以下のように表示されます:
int _fib(int n, int* cache) {
if(n<=0) return 0;
if(n<=2) return 1;
int num1 = 0;
int num2 = 0;
if(cache[n-1] != -1) {
num1 = cache[n-1];
}
else {
num1 = _fib(n-1);
cache[n-1] = num1;
}
if(cache[n-2] !=-1)
{
num2 = cache[n-2];
}
else {
num2 = _fib(n-2);
cache[n-2] = num2;
}
return num1 + num2;
}
int fib(int n) {
int *cache = (int*)malloc(sizeof(int)*n);
for(int i =0;i<n;i++) {
cache[i] = -1;
}
return _fib(n, cache);
}
Fib ボトムアップ法
フィボナッチは小さなスケールで知られているため、大きなスケールは fib(1) と fib(2) に基づいて取得できるため、ボトムアップ法を使用して実現できます。
つまり、fib(n) を求めるには、fib(1)+fib(2) から fib(3) を計算し、次に fib(n) まで、fib(2)+fib(3) から fib(4) を求めます。 )。
コードは次のとおりです。
int fib(int n) {
if (n<=0) return 0;
if (n<=2) return 1;
int num1 = 1;
int num2 = 1;
for(int i = 2;i<n;i++) {
int tmp = num1+num2;
num1 = num2;
num2 = tmp;
}
return num2;
}