动态规划算法入门
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
动态规划算法⼊门
1. 动态规划算法定义:
动态规划,英⽂描述为Dynamic programming. 是⼀种可以把原始问题分解为若⼲相关联的⼦解问题,并通过求取和保存⼦问题的解,获得原问题的解。
动态规划算法可以解决的问题通常包含如下特征:
重叠⼦问题
最优⼦结构
对于第⼀个特征,⽐较容易理解,即分解的若⼲⼦问题,包含着重复的解。
举例如:斐波那契数列,F(n) = F(n-1) + F(n-2),求解的
F(n-1)的过程中,包含着求解F(n-2)的结果。
对于第⼆个特征,参考⽹上的说法为:
假设当前决策结果是f[n],则最优⼦结构就是要让f[n-k]最优,最优⼦结构性质就是能让转移到n的状态是最优的,并且与后⾯的决策没有关系,即让后⾯的决策安⼼地使⽤前⾯的局部最优解的⼀种性质。
关键字解读为:
当前的决策与后⾯的决策是⽆关的,
f[n-k]是最优的,转移到f[n]的状态是最优的
2. 动态规划算法的⼀般步骤和难点
使⽤动态规划算法解决问题的⼀般步骤是:
找到问题的最优解的性质,⽤数学公式或者算法描述
拆解⼦问题,确定问题的递推结构,保证可以收敛。
⽤知乎⼤神们的总结就是:找到问题的状态描述和状态转移⽅程。
3. 动态规划算法的分类和理解
根据我的理解,以及⽹上的说法,我把动态规划算法分为三个类别和层次:
简单动态规划算法,即状态⽅程是⽤⼀个维度的变量的描述的,常见的问题如:斐波那契数列,爬台阶问题等
爬台阶问题问题描述:有⼀座⾼度是10级台阶的楼梯,从下往上⾛,每跨⼀步只能向上1级或者2级台阶。
要求⽤程序来求出⼀共有多少种⾛法。
状态描述:我们使⽤变量n表⽰台阶的级数,F(n)表⽰n级台阶⼀共有多少种⾛法
状态转移⽅程与问题分解:根据每次能跨越的台阶数⽬:1级台阶或者2级台阶,因为⾛到N级台阶之前,⼈⼀定是处于N-1级台阶或者N-2级台阶。
F(n)的⾛法,⼀定是n-1级别的台阶的所有的⾛法和n-2级别台阶的所有⾛法之和。
F(n) = F(n-1) + F(n-2); 关于状态的分解,更详细的说明,可以看这篇⽂章:。
作者讲的⾮常的通俗易懂。
佩服这么⾟苦的编辑。
Java的代码实现
public static int getSumStep(int n){
if(n < 1){
return 0;
}
else if(n == 1){
return 1;
}
else if(n == 2){
return 1;
} else {
int f1 = 1;
int f2 = 1;
int f = 0;
for(int i=3; i<=n; ++i){
f = f1 + f2;
f1 = f2;
f2 = f;
}
return f;
}
}
⼆维的变量变化的动态规划算法,即最优解和递推关系需要两个维度变量来描述的,⽐如01背包问题,两个字符串的公共⼦序列问题
这类问题通常需要两个维度的变量,状态的描述⽐较晦涩,不容易理解,递推关系不是很直观。
我⾃⼰的学习⽅法是牢记⼀个例⼦,这⾥以01背包问题为例:
问题描述:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,5,它们的价值分别是3,4,5,6,现在给你个承重为8的背包,如何让背包⾥装⼊的物品具有最⼤的价值总和?
编号a b c d
w(重量)2345
v(价值)3456
这类问题我觉得抽象的⽐较好的⼀篇⽂章是这篇⽂章:
,不过我当时是在⼿机上看到的,好了好久才找到这篇⽂章。
作者抽象的实在太好了,我觉得我都没法⽤语⾔去写出这么严格的数学公式表达和证明,这⾥就不赘述了。
下⾯写的,仅供⾃⼰理解使⽤,总结下来就是:
X i的取值为0,1 ;表⽰物品是否选取, i的取值为 1,2,3,4表⽰a,b,c,d4见物品
Wi表⽰物品的重量, w1=2, 表⽰ a物品的重量为2
Vi 表⽰物品的价值, v3 - 5, 表⽰物品c的价值为5;
其中n 表⽰前 n个物品,这个表述是很重要的,如果是第⼀次思考这个问题,很多⼈都会卡在这⾥,
m表⽰背包的重量;
约束条件:
递推关系:
第⼀个公式表⽰ n == 0 或者 m == 0 , 即物品的数量为0 或者背包的重量为0的时候,可以算是起始条件
第⼆个公式表⽰:表⽰包的重量⼩于新增加的物品,新增加的物品,⽆法装⼊,如下图的F(2, 2 ) 表⽰前两个物品,包的重量2 , 2 < (w[2] = 3),此时F(2,2 )= F(1 , 2) = 3;
第三个公式表⽰:包的重量能够容纳w[n],新增加的物品,这个时候,最⼤的价值就要在 F(n-1, m) 和 F(n-1, m- Wn) + V[n]) 这两个价值中选取了。
举例如下图打表的 F(4, 8), 因为 8 - (w[n] ,4) > 0 F(4, 8) = max(F(3, 8), F(3,3 ) + v[4]) = 10;
表的过程如下:
java代码如下:
public static int getMaxValue(int[] wArray, int[] vArray, int bagWeight){
int lenght = wArray.length;
// init set zero
// manipulator the talbe
int [][] result = new int[lenght+1][bagWeight+1];
int [][] bRecord = new int[lenght+1][bagWeight+1];
for(int i=1; i<= lenght; ++i){
for(int j=1;j <= bagWeight; ++j){
if(j<wArray[i-1]){
result[i][j] = result[i-1][j];
bRecord[i][j] = 1;
}else{
if( result[i-1][j] > result[i-1][j-wArray[i-1]]+ vArray[i-1]) {
result[i][j] = result[i-1][j];
bRecord[i][j] = 1;
} else{
result[i][j] = result[i-1][j-wArray[i-1]]+ vArray[i-1];
bRecord[i][j] = 2;
}
}
}
}
return result[lenght][bagWeight];
//return bRecord;
}
需要注意的是因为java数组的索引下标为从0,开始,所以
result[i][j] = result[i-1][j-wArray[i-1]]+ vArray[i-1];
brecord是记录操作的过程,⽤于回溯使⽤,这部分代码,后续实现。
带有额外条件的动态规划问题(这类问题,我暂时还没有学习)
4. 动态规划与分治法的区别和联系
分治法是指将问题划分成⼀些独⽴地⼦问题,递归地求解各⼦问题,然后合并⼦问题的解⽽得到原问题的解。
动态规划适⽤于⼦问题独⽴且重叠的情况,也就是各⼦问题包含公共的⼦⼦问题。
动态规划算法对每个⼦⼦问题只求解⼀次,将其结果保存在⼀张表中,从⽽避免每次遇到各个⼦问题时重新计算答案。
分治法主要在于⼦问题的独⽴性,⽐如排序算法等,动态规划算法主要适⽤于处理⼦问题重复性和最优⼦结构的的问题。
⽬前的理解还⽐较浅显,只能先这么记录了。