動態規劃的關鍵點:
- 最優化原理,也就是最優子結構性質。這指的是一個最優化策略具有這樣的性質,無論過去狀態和決策如何,對前面的決策所形成的狀態而言,余下的決策必須構成最優策略,簡單來說就是一個最優化策略的子策略總是最優的,如果一個問題滿足最優化原理,就稱其有最優子結構性質。
- 無后效性,指的是某個狀態下的決策的收益,只與狀態和決策相關,與達到該狀態的方式無關。
- 子問題的重疊性,動態規劃將原來指數級的暴力搜索算法改進到了具有多項式時間復雜度的算法,其中的關鍵在于解決了冗余、重復計算的問題,這是動態規劃算法的根本目的。
- 總體來說,動態規劃算法就是一系列以空間換取時間的算法。
動態規劃使用
下面通過遞歸方式計算問題斐波那契數列(Fibonacci sequence)來闡述分治法不是銀彈。
斐波那契數列指的是這樣一個數列 0,1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........
這個數列從第3項開始,每一項都等于前兩項之和。
下面給出遞歸實現的代碼:
/**
* 求解Fibonacci數列中第n個元素的值
* @param n 斐波那契數列中的位置
* @return Fibonacci數列中第n個元素的值
*/
public int fibonacci(int n) {
if(n<2) return n;
if(n>=2) {
return fibonacci(n-1)+fibonacci(n-2);
}
return -1;
}
通過控制臺驗證下算法的計算結果和計算用時。
fibonacci 0 is 0 計算用時 0ms
fibonacci 1 is 1 計算用時 1ms
fibonacci 2 is 1 計算用時 0ms
fibonacci 3 is 2 計算用時 0ms
fibonacci 4 is 3 計算用時 0ms
fibonacci 5 is 5 計算用時 0ms
fibonacci 6 is 8 計算用時 0ms
fibonacci 7 is 13 計算用時 0ms
fibonacci 8 is 21 計算用時 0ms
fibonacci 9 is 34 計算用時 0ms
fibonacci 10 is 55 計算用時 0ms
fibonacci 11 is 89 計算用時 0ms
fibonacci 12 is 144 計算用時 0ms
fibonacci 13 is 233 計算用時 0ms
fibonacci 14 is 377 計算用時 0ms
fibonacci 15 is 610 計算用時 0ms
fibonacci 16 is 987 計算用時 0ms
fibonacci 17 is 1597 計算用時 1ms
fibonacci 18 is 2584 計算用時 0ms
fibonacci 19 is 4181 計算用時 0ms
fibonacci 20 is 6765 計算用時 0ms
fibonacci 21 is 10946 計算用時 0ms
fibonacci 22 is 17711 計算用時 0ms
fibonacci 23 is 28657 計算用時 1ms
fibonacci 24 is 46368 計算用時 0ms
fibonacci 25 is 75025 計算用時 1ms
fibonacci 26 is 121393 計算用時 1ms
fibonacci 27 is 196418 計算用時 1ms
fibonacci 28 is 317811 計算用時 3ms
fibonacci 29 is 514229 計算用時 2ms
fibonacci 30 is 832040 計算用時 4ms
fibonacci 31 is 1346269 計算用時 8ms
fibonacci 32 is 2178309 計算用時 11ms
fibonacci 33 is 3524578 計算用時 20ms
fibonacci 34 is 5702887 計算用時 29ms
fibonacci 35 is 9227465 計算用時 46ms
fibonacci 36 is 14930352 計算用時 73ms
fibonacci 37 is 24157817 計算用時 127ms
fibonacci 38 is 39088169 計算用時 186ms
fibonacci 39 is 63245986 計算用時 306ms
fibonacci 40 is 102334155 計算用時 505ms
fibonacci 41 is 165580141 計算用時 816ms
fibonacci 42 is 267914296 計算用時 1298ms
fibonacci 43 is 433494437 計算用時 2110ms
fibonacci 44 is 701408733 計算用時 3423ms
fibonacci 45 is 1134903170 計算用時 5525ms
fibonacci 46 is 1836311903 計算用時 9013ms
fibonacci 47 is -1323752223 計算用時 14382ms
可以看出,n=47是出現了int類型的越界(Integer.MAX_VALUE is 2147483647)這里需要注意,計算用時達到了恐怖的14秒多。斐波那契數列的值隨著增大進行指數級的增長,運行事件也是指數級的增長(n越大越能看出這一點)。
動態規劃版本
正如魯迅先生的《狂人日記》中,孔乙己"回"字的四種寫法。動態規劃算法有兩種等價的實現方法。
- 帶備忘的自頂向下法(top-down with memoization),此方法仍按自然的遞歸形式編寫過程,但過程會保存每個子問題的解(通常保存在一個數組或者散列表中)。當需要一個子問題的解時,過程首先檢查是否已經保存過此解。如果是,則直接返回保存的值,從而節省了計算時間,否則,按通常的方式計算這個子問題。我們稱這個遞歸過程是帶備忘的(memoized),因為它“記住”了之前計算出的結果。
以斐波那契數列的解法為例,代碼如下:
long[] memo=new long[memoSize];
public long topdown(int n) {
if(n<0) return -1;
if(n<2) return n;
if(memo[n]>0) {
return memo[n];
}else {
return memo[n]=topdown(n-1)+topdown(n-2);
}
通過控制臺驗證下算法的計算結果和計算用時。
fibonacci 0 is 0 計算用時 0ms
fibonacci 1 is 1 計算用時 0ms
fibonacci 2 is 1 計算用時 0ms
fibonacci 3 is 2 計算用時 1ms
fibonacci 4 is 3 計算用時 0ms
fibonacci 5 is 5 計算用時 0ms
fibonacci 6 is 8 計算用時 0ms
fibonacci 7 is 13 計算用時 0ms
fibonacci 8 is 21 計算用時 0ms
fibonacci 9 is 34 計算用時 0ms
fibonacci 10 is 55 計算用時 0ms
fibonacci 11 is 89 計算用時 0ms
fibonacci 12 is 144 計算用時 0ms
fibonacci 13 is 233 計算用時 0ms
fibonacci 14 is 377 計算用時 0ms
fibonacci 15 is 610 計算用時 0ms
fibonacci 16 is 987 計算用時 0ms
fibonacci 17 is 1597 計算用時 0ms
fibonacci 18 is 2584 計算用時 0ms
fibonacci 19 is 4181 計算用時 0ms
fibonacci 20 is 6765 計算用時 0ms
fibonacci 21 is 10946 計算用時 0ms
fibonacci 22 is 17711 計算用時 0ms
fibonacci 23 is 28657 計算用時 0ms
fibonacci 24 is 46368 計算用時 0ms
fibonacci 25 is 75025 計算用時 0ms
fibonacci 26 is 121393 計算用時 0ms
fibonacci 27 is 196418 計算用時 0ms
fibonacci 28 is 317811 計算用時 0ms
fibonacci 29 is 514229 計算用時 0ms
fibonacci 30 is 832040 計算用時 0ms
fibonacci 31 is 1346269 計算用時 0ms
fibonacci 32 is 2178309 計算用時 0ms
fibonacci 33 is 3524578 計算用時 0ms
fibonacci 34 is 5702887 計算用時 0ms
fibonacci 35 is 9227465 計算用時 0ms
fibonacci 36 is 14930352 計算用時 0ms
fibonacci 37 is 24157817 計算用時 0ms
fibonacci 38 is 39088169 計算用時 0ms
fibonacci 39 is 63245986 計算用時 0ms
fibonacci 40 is 102334155 計算用時 0ms
fibonacci 41 is 165580141 計算用時 0ms
fibonacci 42 is 267914296 計算用時 0ms
fibonacci 43 is 433494437 計算用時 0ms
fibonacci 44 is 701408733 計算用時 0ms
fibonacci 45 is 1134903170 計算用時 0ms
fibonacci 46 is 1836311903 計算用時 0ms
fibonacci 47 is 2971215073 計算用時 0ms
- 自底向上法(bottom-up method),這種方法一般需要恰當定義子問題的“規?!钡母拍睿沟萌魏巫訂栴}的求解只依賴“更小的”子問題的求解。因而我們可以將子問題按規模排序,按由小到大的順序進行求解。當求解某個子問題時,它所依賴的那些更小的子問題都已經求解完畢,結果已經保存。每個子問題只需求解依次,當我們求解它(也是第一次遇到它)時,它的所有前提子問題都已求解完成。
動態規劃的解法-自底向上法
long[] memo=new long[memoSize];
public long bottomup(int n) {
if(n<0) return -1;
if(n<=1) return n;
memo[0]=0;
memo[1]=1;
if(n>1) {
for(int i=2;i<=n;i++) {
memo[i]=memo[i-1]+memo[i-2];
}
}
return memo[n];
}
通過控制臺驗證下算法的計算結果和計算用時。
fibonacci 0 is 0 計算用時 0ms
fibonacci 1 is 1 計算用時 0ms
fibonacci 2 is 1 計算用時 0ms
fibonacci 3 is 2 計算用時 0ms
fibonacci 4 is 3 計算用時 0ms
fibonacci 5 is 5 計算用時 0ms
fibonacci 6 is 8 計算用時 0ms
fibonacci 7 is 13 計算用時 0ms
fibonacci 8 is 21 計算用時 0ms
fibonacci 9 is 34 計算用時 0ms
fibonacci 10 is 55 計算用時 0ms
fibonacci 11 is 89 計算用時 0ms
fibonacci 12 is 144 計算用時 0ms
fibonacci 13 is 233 計算用時 0ms
fibonacci 14 is 377 計算用時 0ms
fibonacci 15 is 610 計算用時 0ms
fibonacci 16 is 987 計算用時 0ms
fibonacci 17 is 1597 計算用時 0ms
fibonacci 18 is 2584 計算用時 0ms
fibonacci 19 is 4181 計算用時 0ms
fibonacci 20 is 6765 計算用時 0ms
fibonacci 21 is 10946 計算用時 0ms
fibonacci 22 is 17711 計算用時 0ms
fibonacci 23 is 28657 計算用時 0ms
fibonacci 24 is 46368 計算用時 0ms
fibonacci 25 is 75025 計算用時 0ms
fibonacci 26 is 121393 計算用時 0ms
fibonacci 27 is 196418 計算用時 0ms
fibonacci 28 is 317811 計算用時 0ms
fibonacci 29 is 514229 計算用時 0ms
fibonacci 30 is 832040 計算用時 0ms
fibonacci 31 is 1346269 計算用時 0ms
fibonacci 32 is 2178309 計算用時 0ms
fibonacci 33 is 3524578 計算用時 0ms
fibonacci 34 is 5702887 計算用時 0ms
fibonacci 35 is 9227465 計算用時 0ms
fibonacci 36 is 14930352 計算用時 0ms
fibonacci 37 is 24157817 計算用時 0ms
fibonacci 38 is 39088169 計算用時 0ms
fibonacci 39 is 63245986 計算用時 0ms
fibonacci 40 is 102334155 計算用時 0ms
fibonacci 41 is 165580141 計算用時 0ms
fibonacci 42 is 267914296 計算用時 0ms
fibonacci 43 is 433494437 計算用時 0ms
fibonacci 44 is 701408733 計算用時 0ms
fibonacci 45 is 1134903170 計算用時 0ms
fibonacci 46 is 1836311903 計算用時 0ms
fibonacci 47 is 2971215073 計算用時 0ms
總結
同樣規模的斐波那契數列的計算問題,動態規劃版本的都能在很短的時間內計算完畢,遞歸的解決方案,在小于50的情況下就出現了十幾秒的計算情況,動態規劃的運行時間的提升很明顯。
動態規劃解決走臺階問題
有n級臺階,每次上一級或者兩級,問有多少種走完n級臺階的方法。
分析:動態規劃的實現的關鍵在于能不能準確合理的用動態規劃表來抽象出 實際問題。在這個問題上,我們讓f(n)表示走上n級臺階的方法數。
那么當n為1時,f(n) = 1,n為2時,f(n) =2,就是說當臺階只有一級的時候,方法數是一種,臺階有兩級的時候,方法數為2。那么當我們要走上n級臺階,必然是從n-1級臺階邁一步或者是從n-2級臺階邁兩步,所以到達n級臺階的方法數必然是到達n-1級臺階的方法數加上到達n-2級臺階的方法數之和。即f(n) = f(n-1)+f(n-2),我們用dp[n]來表示動態規劃表,dp[i],i>0,i<=n,表示到達i級臺階的方法數。
動態規劃的解法-自底向上法
long[] memo=new long[memoSize];
public long stepBottomup(int n) {
if(n<0) return 0;
if(n==1) return 1;
if(n==2) return 2;
memo[0]=1;
memo[1]=2;
if(n>1) {
for(int i=2;i<n;i++) {
memo[i]=memo[i-1]+memo[i-2];
}
}
return memo[n-1];
}
通過控制臺驗證下算法的計算結果和計算用時。
走臺階方案數 樓梯數 1 走法 is 1 計算用時 0ms
走臺階方案數 樓梯數 2 走法 is 2 計算用時 0ms
走臺階方案數 樓梯數 3 走法 is 3 計算用時 0ms
走臺階方案數 樓梯數 4 走法 is 5 計算用時 0ms
走臺階方案數 樓梯數 5 走法 is 8 計算用時 0ms
走臺階方案數 樓梯數 6 走法 is 13 計算用時 0ms
走臺階方案數 樓梯數 7 走法 is 21 計算用時 0ms
走臺階方案數 樓梯數 8 走法 is 34 計算用時 0ms
走臺階方案數 樓梯數 9 走法 is 55 計算用時 0ms
走臺階方案數 樓梯數 10 走法 is 89 計算用時 0ms
走臺階方案數 樓梯數 11 走法 is 144 計算用時 0ms
走臺階方案數 樓梯數 12 走法 is 233 計算用時 0ms
走臺階方案數 樓梯數 13 走法 is 377 計算用時 0ms
走臺階方案數 樓梯數 14 走法 is 610 計算用時 0ms
走臺階方案數 樓梯數 15 走法 is 987 計算用時 0ms
走臺階方案數 樓梯數 16 走法 is 1597 計算用時 0ms
走臺階方案數 樓梯數 17 走法 is 2584 計算用時 0ms
走臺階方案數 樓梯數 18 走法 is 4181 計算用時 0ms
走臺階方案數 樓梯數 19 走法 is 6765 計算用時 0ms
走臺階方案數 樓梯數 20 走法 is 10946 計算用時 0ms
走臺階方案數 樓梯數 21 走法 is 17711 計算用時 0ms
走臺階方案數 樓梯數 22 走法 is 28657 計算用時 0ms
走臺階方案數 樓梯數 23 走法 is 46368 計算用時 0ms
走臺階方案數 樓梯數 24 走法 is 75025 計算用時 1ms
走臺階方案數 樓梯數 25 走法 is 121393 計算用時 0ms
走臺階方案數 樓梯數 26 走法 is 196418 計算用時 0ms
走臺階方案數 樓梯數 27 走法 is 317811 計算用時 0ms
走臺階方案數 樓梯數 28 走法 is 514229 計算用時 0ms
走臺階方案數 樓梯數 29 走法 is 832040 計算用時 0ms
走臺階方案數 樓梯數 30 走法 is 1346269 計算用時 0ms
走臺階方案數 樓梯數 31 走法 is 2178309 計算用時 0ms
走臺階方案數 樓梯數 32 走法 is 3524578 計算用時 0ms
走臺階方案數 樓梯數 33 走法 is 5702887 計算用時 0ms
走臺階方案數 樓梯數 34 走法 is 9227465 計算用時 0ms
走臺階方案數 樓梯數 35 走法 is 14930352 計算用時 0ms
走臺階方案數 樓梯數 36 走法 is 24157817 計算用時 0ms
走臺階方案數 樓梯數 37 走法 is 39088169 計算用時 0ms
走臺階方案數 樓梯數 38 走法 is 63245986 計算用時 0ms
走臺階方案數 樓梯數 39 走法 is 102334155 計算用時 0ms
走臺階方案數 樓梯數 40 走法 is 165580141 計算用時 0ms
走臺階方案數 樓梯數 41 走法 is 267914296 計算用時 0ms
走臺階方案數 樓梯數 42 走法 is 433494437 計算用時 0ms
走臺階方案數 樓梯數 43 走法 is 701408733 計算用時 1ms
走臺階方案數 樓梯數 44 走法 is 1134903170 計算用時 0ms
走臺階方案數 樓梯數 45 走法 is 1836311903 計算用時 0ms
走臺階方案數 樓梯數 46 走法 is 2971215073 計算用時 0ms
走臺階方案數 樓梯數 47 走法 is 4807526976 計算用時 0ms
這里只給出其中的一種解法,至于另一種解法,大家嘗試的寫下。
以上,謝謝閱讀,希望你有所收獲!
算法導論公開課筆記(一)算法分析與設計
算法導論公開課筆記(二)快速排序和隨機化算法
算法導論公開課筆記(三)線性時間排序
算法導論公開課筆記(四)順序統計、中值
動態規劃算法
參考鏈接
動態規劃算法經典案例