任何數學遞推公式均可以直接轉換成遞推算法,可是編譯器經常不能正確對待遞歸算法。將遞歸從新寫成非遞歸算法,讓後者把些子問題的答案系統地記錄在一個表內。利用這種方法的一種技巧叫作動態規劃java
注:由已知推未知就是遞推,由未知推未知就是遞歸,這裏說的數學遞推公式有別與遞推算法。具體解釋以下: 若是數列{an}的第n項與它前一項或幾項的關係能夠用一個式子來表示,那麼這個公式叫作這個數列的遞推公式。git
有通項公式 f(n)=f(n-1)+f(n-2); f(0)=f(1)=1;求任意n對應的f(n)github
注意:目前有的編譯器能夠優化尾遞歸算法
/**
* 遞歸實現違反合成效益法則
* */
public static int fib(int n){
if(n<=1){
return 1;
}else{
return fib(n-1)+fib(n-2);
}
}
複製代碼
以求f6爲例,計算f6須要計算f5和f4,而算f5是有須要計算f4+f3,則一定有重複計算的部分。具體詳細見下圖,(下圖紫色部分都是多餘計算)數組
因爲計算F(N)只須要知道F(N-1)和F(N-2),所以咱們只須要保留最近算出的兩個斐波那契數,並從f(2)開始一直計算的f(n)便可。bash
/**
* 動態規劃版本,保證沒有多餘的計算,
* 以last 保存f(i-1)的值,nextToLast保存f(i-2)
* answer 保存f(i)的值
* */
public static int fibonacci(int n){
if(n<=1){
return 1;
}
int last=1;
int nextToLast=1;
int answer=1;
for(int i=2;i<=n;i++){
answer=last+nextToLast;
nextToLast=last;
last=answer;
}
return answer;
}
複製代碼
假定揹包的最大容量爲W,N件物品,每件物品都有本身的價值val和重量wt,將物品放入揹包中使得揹包內物品的總價值最大(val的和最大)。數據結構
臨時揹包總價值=Max{選取當前項揹包總價值,不選取當前項揹包總價值},轉換爲數學公式爲:優化
選取當前項時, 臨時揹包總價值=val[item-1]+V[item-1][weight-wt[item-1]]ui
不選取當前項,臨時揹包總價值= V[item-1][weight]spa
V[item][weight]=Math.max (val[item-1]+V[item-1][weight-wt[item-1]], V[item-1][weight]);
複製代碼
進過上步驟分析,咱們僅需保留以item爲行,以權重weight爲列的二維數組便可。具體實現以下:
/**
* @param val 權重數組
* @param wt 重量數組
* @param W 總權重
* @return 揹包中使得揹包內物品的總價值最大時的重量
*/
public static int knapsack(int val[], int wt[], int W) {
//物品數量總和
int N = wt.length;
//建立一個二維數組
//行最多存儲N個物品,列最多爲總權重W,下邊N+1和W+1是保證從1開始
int[][] V = new int[N + 1][W + 1];
//將行爲 0或者列爲0的值,都設置爲0
for (int col = 0; col <= W; col++) {
V[0][col] = 0;
}
for (int row = 0; row <= N; row++) {
V[row][0] = 0;
}
//從1開始遍歷N個物品
for (int item=1;item<=N;item++){
//一行一行的填充數據
for (int weight=1;weight<=W;weight++){
if (wt[item-1]<=weight){
//選取(當前項值+以前項去掉當前項權重的值)與不取當前項的值得最大者
V[item][weight]=Math.max (val[item-1]+V[item-1][weight-wt[item-1]], V[item-1][weight]);
}else {//不選取當前項,以以前項代替
V[item][weight]=V[item-1][weight];
}
}
}
//打印最終矩陣
for (int[] rows : V) {
for (int col : rows) {
System.out.format("%5d", col);
}
System.out.println();
}
//返回結果
return V[N][W];
}
複製代碼