迎来了新的课题,动态规划,听这个名字就很高大上,因为会动啊。
概念&基本思想
多阶段决策过程的最优化问题。是一种途径,而不是一种特殊算法,没有准确的数学表达式,所以叫动态的嘛。
多阶段决策问题
如果一类活动可以分解为若干相互联系的阶段,在每个阶段都做出决策,一个阶段的决策做出以后影响下一个阶段的决策,从而完全确定了一条决策路线,这成为多阶段决策问题。
各个阶段的决策构成一个序列,构成一个策略。每个阶段的决策不同就构成了不同的策略,多阶段决策问题就是要确定一个最优策略。
术语
阶段:把所给求解问题的过程恰当地分成若干个相互联系的阶段,以便于求解,过程不同,阶段数就可能不同.描述阶段的变量称为阶段变量。在多数情况下,阶段变量是离散的,用k表示。此外,也有阶段变量是连续的情形。如果过程可以在任何时刻作出决策,且在任意两个不同的时刻之间允许有无穷多个决策时,阶段变量就是连续的。
状态:状态表示每个阶段开始面临的自然状况或客观条件。过程的状态通常可以用一个或一组数来描述,称为状态变量。一般,状态是离散的,但有时为了方便也将状态取成连续的。
无后效性:如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,所有各阶段都确定时,整个过程也就确定了。
决策:一个阶段的状态给定以后,从该状态演变到下一阶段某个状态的一种选择(行动)称为决策。描述决策的变量称决策变量,因状态满足无后效性,故在每个阶段选择决策时只需考虑当前的状态而无须考虑过程的历史。决策变量的范围称为允许决策集合。
策略:由每个阶段的决策组成的序列称为策略。对于每一个实际的多阶段决策过程,可供选取的策略有一定的范围限制,这个范围称为允许策略集合。允许策略集合中达到最优效果的策略称为最优策略。
基本思想
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。
(百度百科)
把求解的问题分成许多阶段或多个子问题,然后按顺序求解各个子问题。前一个子问题的解为后一个子问题的求解提供了有用的信息。在求解任何一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解,依次解决各子问题,最后一个子问题就是问题的解。
基本要素 || 适用条件
- 最优子结构:原问题最优解包含子问题的最优解。或者说,一个最优化策略的子策略总是最优的。
- 子问题重叠:每次产生的子问题并不总是新的,有些子问题被重复计算。动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术。
- 无后效性
基本模型 || 基本步骤
- 确定问题的决策对象
- 对决策过程进行阶段划分
- 确定各个阶段的状态变量
- 根据状态变量确定目标函数和费用函数
- 建立各个阶段状态变量的转移过程,确定状态转移方程。
(百度百科)
- 找出最优解性质
- 递归地定义最优值
- 自底向上的方式计算最优值(自底向上可以将重复的计算保存起来)
- 根据计算最优值得到的信息,构造最优解。
例子
矩阵连乘
问题描述:给定n个矩阵{A1,A2,…,An},其中,Ai与Ai+1是可乘的,(i=1,2 ,…,n-1)。用加括号的方法表示矩阵连乘的次序,不同的计算次序计算量(乘法次数)是不同的,找出一种加括号的方法,使得矩阵连乘的次数最小。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少乘法次数。
参考:https://blog.csdn.net/liufeng_king/article/details/8497607、https://blog.csdn.net/x_xhuashui/article/details/81903558
问题解析:
这道题需要找到一个矩阵相乘的顺序,使得乘法运算的次数最小。每次两个相乘的矩阵构成一个阶段,每一次决定哪两个矩阵相乘,即为一个决策,每次相乘之后的现状即为状态。
设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]。
当i=j时,A[i:j]=Ai,因此,m[i][i]=0,i=1,2,…,n
当i<j时,若A[i:j]的最优次序在Ak和Ak+1之间断开,i<=k<j,则:m[i][j]=m[i][k]+m[k+1][j]+pi-1pkpj。由于在计算是并不知道断开点k的位置,所以k还未定。不过k的位置只有j-i个可能。因此,k是这j-i个位置使计算量达到最小的那个位置。
递推关系:
感觉有点儿难。。稍后再看看
公共最长子序列(LCS)
问题描述:
给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。注,子序列不一定连续。比如字符串1:BDCABA;字符串2:ABCBDAB,则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA
参考:https://www.cnblogs.com/hapjin/p/5572483.html
分析:动态规划问题的基本要素:重叠子问题、最优子结构。
递推关系:
c[i,j]表示:(x1,x2....xi) 和 (y1,y2...yj) 的最长公共子序列的长度
代码实现:
public static int LCS(String str1, String str2){
int[][] c = new int[str1.length() + 1][str2.length() + 1]; //这个表用来存储计算的值
for(int row = 0; row <= str1.length(); row++)
c[row][0] = 0;
for(int column = 0; column <= str2.length(); column++)
c[0][column] = 0;
for(int i = 1; i <= str1.length(); i++)
for(int j = 1; j <= str2.length(); j++)
{
if(str1.charAt(i-1) == str2.charAt(j-1))
c[i][j] = c[i-1][j-1] + 1;
else if(c[i][j-1] > c[i-1][j])
c[i][j] = c[i][j-1];
else
c[i][j] = c[i-1][j];
}
return c[str1.length()][str2.length()];
}
很明显,使用空间换时间。虽然递推关系式是从后往前,但是计算还是从前往后计算。
0-1背包问题
问题描述:
0-1 背包问题:给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi,其价值为 vi 。
问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?
参考:https://blog.csdn.net/mu399/article/details/7722810
分析:重叠子问题、最优子结构
递推关系式:Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积,定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值
代码实现:
待完善