将决策过程分为若干个相互联系的阶段,每个阶段都需要做出某项决策,决策的选取要依赖当前面临的状态,并影响后面决策的发展,从而使整个过程达到最好的活动效果。
把一个问题看作是一个前后关联具有链状结构的多阶段过程称为多阶段决策过程。
决策依赖于当前面临的状态,随即引起状态的转移,各阶段决策确定后组成的一个决策序列。
决策序列是在变化的状态中产生,故有“动态含义”。处理它的方法称为动态规划方法。
阶段:
将所给问题的过程,按时间或空间特征分解成若干个相互联系的阶段,以便按次序去求每个阶段的解。描述阶段的变量称为阶段变量,常用k表示阶段变量。
状态:
各阶段开始时的自然状况和客观条件叫做状态。描述阶段状态的变量称为状态变量,常用 s k {s_k} sk表示第k阶段的状态变量。
无后效性:当某阶段状态给定以后,在这阶段以后的过程的发展不受这段以前各段状态的影响。即过程的过去历史只能通过当前状态去影响它未来的发展,这称为无后效性。如果所选定的状态变量不具备无后效性,就不能作为状态变量来构造动态规划模型。
决策:
当各阶段的状态确定以后,可以做出不同的决定或选择,从而确定下个阶段的状态,这种决定称为决策。描述决策的变量称为决策变量,用 u k ( s k ) {u_k}\left( { {s_k}} \right) uk(sk)。
策略:
策略是按顺序排列的决策组成的集合。由过程的第k阶段开始到终止状态为止的过程,称为问题的后部子过程(或称为k子过程)。由每段的决策按照顺序排列组成的决策函数序列 { u k ( s k ) , u k + 1 ( s k + 1 ) , . . . , u n ( s n ) } \left\{ { {u_k}\left( { {s_k}} \right),{u_{k + 1}}\left( { {s_{k + 1}}} \right),...,{u_n}\left( { {s_n}} \right)} \right\} { uk(sk),uk+1(sk+1),...,un(sn)}称为k子过程策略,简称为子策略,记为 p 1 , n ( s 1 ) {p_{1,n}}\left( { {s_1}} \right) p1,n(s1),即 p 1 , n ( s 1 ) = { u 1 ( s 1 ) , u 2 ( s 2 ) , . . . , u n ( s n ) } {p_{1,n}}\left( { {s_1}} \right) = \left\{ { {u_1}\left( { {s_1}} \right),{u_2}\left( { {s_2}} \right),...,{u_n}\left( { {s_n}} \right)} \right\} p1,n(s1)={ u1(s1),u2(s2),...,un(sn)}
在实际问题中,可供选择的策略有一定的范围, 此范围称为允许策略集合, 用 P P P表示。从允许策略集合中找出达到最优效果的策略称为最优策略。
最优化原理:一个最优化策略的子策略总是最优的。所以不管过去的过程如何,只从当前的状态和系统的最优化要求出发,作出下一步的最优决策。
状态转移方程:
动态规划中本阶段的状态是上一个阶段的决策结果。如果给定了第k段的状态。如果给定了第 k k k段的状态 s k {s_k} sk,本阶段决策为 u k ( s k ) {u_k}\left( { {s_k}} \right) uk(sk),则第k+1段的状态 s k + 1 {s_{k + 1}} sk+1由公式: s k + 1 = T k ( s k , u k ) {s_{k + 1}} = {T_k}\left( { {s_k},{u_k}} \right) sk+1=Tk(sk,uk)确定,称为状态转移方程。 T k {T_k} Tk为状态转移函数。
指标函数:
用于衡量所选定策略优劣的数量指标称为指标函数,是定义在全过程和所有后部子过程上确定的数量函数,用 V k , n {V_{k,n}} Vk,n表示,即 V k , n = V k , n ( s k , u k , s k + 1 , u k + 1 , . . . , s n + 1 ) , k = 1 , 2 , . . . , n {V_{k,n}} = {V_{k,n}}\left( { {s_k},{u_k},{s_{k + 1}},{u_{k + 1}},...,{s_{n + 1}}} \right),k = 1,2,...,n Vk,n=Vk,n(sk,uk,sk+1,uk+1,...,sn+1),k=1,2,...,n
该指标函数应该具有可分离性,并满足递推关系,即 V k , n {V_{k,n}} Vk,n可以表示为 s k , u k , V k , n { {s_k},{u_k},{V_{k,n}}} sk,uk,Vk,n的函数。
V k , n = V k , n ( s k , u k , s k + 1 , u k + 1 , . . . , s n + 1 ) = ψ k [ s k , u k , V k + 1 ( s k + 1 , u k + 1 , . . . , s n + 1 ) ] {V_{k,n}} = {V_{k,n}}\left( { {s_k},{u_k},{s_{k + 1}},{u_{k + 1}},...,{s_{n + 1}}} \right) = {\psi _k}[{s_k},{u_k},{V_{k + 1}}\left( { {s_{k + 1}},{u_{k + 1}},...,{s_{n + 1}}} \right)] Vk,n=Vk,n(sk,uk,sk+1,uk+1,...,sn+1)=ψk[sk,uk,Vk+1(sk+1,uk+1,...,sn+1)]
不同的问题中,指标函数的含义不同,可能是距离、利润、成本、产量或资源消耗等等。
最优值函数:
最优值函数记为 f k ( s k ) {f_k}\left( { {s_k}} \right) fk(sk),它表示从第k段状态 s k {s_k} sk采用最优策略 p k , n {p_{k,n}} pk,n到过程终止时的最优效益值。 f k ( s k ) {f_k}\left( { {s_k}} \right) fk(sk)与 V k , n ( s k , p k , n ) {V_{k,n}}\left( { {s_k},{p_{k,n}}} \right) Vk,n(sk,pk,n)的关系为: f k ( s k ) = o p t V k , n ( s k , p k , n ) {f_k}\left( { {s_k}} \right) = opt{\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {V_{k,n}}\left( { {s_k},{p_{k,n}}} \right) fk(sk)=optVk,n(sk,pk,n),其中, o p t opt opt是最优化的缩写,根据题意而取 m i n min min或 m a x max max。
动态规划方法的关键在于正确的写出基本的递推关系式和恰当的边界条件(简言之为基本方程);
要做到这一点,必须先将问题的过程分为几个相互联系的阶段,恰当的选取状态变量和决策变量及定量最优值函数,从而把一个大问题化成一族同类型的子问题,然后逐个求解。即从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行;
最后一个子问题所得到的最优解,就是整个问题的最优解。
线性规划和非线性规划问题,通常与时间无关,故称静态规划。
动态规划问题与时间有关,是研究具有多阶段决策过程的一类问题。对于某些静态规划问题可以引入时间因素,把它看作动态规划问题。
动态规划方法有逆序解法和顺序解法,其关键在于正确写出动态规划的递推关系式。
顺序解法和逆序解法只表示“行进方向”的不同对始端终端看法的颠倒。但用动态规划方法求解最优解时,都是在“行进方向”规定后,均要逆着这个规定的行进方向,从最后一段向前逆推计算,逐段找出最有途径。
步骤1-3是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤4可以省略,若需要求出问题的一个最优解,则必须执行步骤4。此时,在步骤3中计算最优值时,通常需记录更多的信息,以便在步骤4中,根据所记录的信息,快速构造出一个最优解。
通俗而言,可以用以下“四部曲”来使用动态规划算法:
问题必须满足最优化原理和无后效性才适用动态规划。
如果一个问题满足以下两点,那么它就能用动态规划解决。
有三种硬币,分别面值2元,5元和7元,每种硬币都足够多,买一本书需要27元,如何用最少的硬币数量正好付清,不需要对方找钱?
解析:
假设最后一个硬币的面值是 a k {a_k} ak,那么除最后一个硬币外的面值是 27 − a k {27-a_k} 27−ak,虽然我们不知道最优策略是什么,但最优策略肯定是k枚硬币 a 1 , a 2 , . . . , a k {a_1},{a_2},...,{a_k} a1,a2,...,ak面值加起来是27。
我们不关心前面的 k − 1 k - 1 k−1枚硬币是怎么拼出 27 − a k 27 - {a_k} 27−ak的,而且我们现在甚至还不知道 a k {a_k} ak和 k k k,但是我们确定前面的硬币拼出了 27 − a k 27 - {a_k} 27−ak。
原问题是最少用多少枚硬币拼出27,我们将原问题转化成了一个子问题,而且规模小:最少用多少枚硬币可以拼出 27 − a k 27 - {a_k} 27−ak。
为了简化定义,假设最优值函数:
f ( x ) = 拼 出 x 所 用 的 最 少 硬 币 数 f\left( x \right)=拼出x所用的最少硬币数 f(x)=拼出x所用的最少硬币数。
最后那枚硬币 a k {a_k} ak只可能是2,5或者7,
如果 a k {a_k} ak是2, f ( 27 ) f\left( {27} \right) f(27)应该是 f ( 27 − 2 ) + 1 f\left( {27 - 2} \right) + 1 f(27−2)+1(加上最后一枚硬币2)
如果 a k {a_k} ak是5, f ( 27 ) f\left( {27} \right) f(27)应该是 f ( 27 − 5 ) + 1 f\left( {27 - 5} \right) + 1 f(27−5)+1(加上最后一枚硬币5)
如果 a k {a_k} ak是7, f ( 27 ) f\left( {27} \right) f(27)应该是 f ( 27 − 7 ) + 1 f\left( {27 - 7} \right) + 1 f(27−7)+1(加上最后一枚硬币7)
需要求最少的硬币数,所以:
f ( 27 ) = min { f ( 27 − 2 ) + 1 , f ( 27 − 5 ) + 1 , f ( 27 − 7 ) + 1 } f\left( {27} \right) = \min \left\{ {f\left( {27 - 2} \right) + 1,f\left( {27 - 5} \right) + 1,f\left( {27 - 7} \right) + 1} \right\} f(27)=min{ f(27−2)+1,f(27−5)+1,f(27−7)+1}
由此可知方程满足如下关系式:
f ( x ) = min { f ( x − 2 ) + 1 , f ( x − 5 ) + 1 , f ( x − 7 ) + 1 } f\left( x \right) = \min \left\{ {f\left( {x - 2} \right) + 1,f\left( {x - 5} \right) + 1,f\left( {x - 7} \right) + 1} \right\} f(x)=min{ f(x−2)+1,f(x−5)+1,f(x−7)+1}
接下来确定初始和边界问题,显然, f ( 0 ) = 0 f\left( 0 \right) = 0 f(0)=0,即需要0枚硬币拼出0。
那当 x − 2 , x − 5 , x − 7 x - 2,x - 5,x - 7 x−2,x−5,x−7小于0时怎么办?
令 f ( x ) = + ∞ ( x < 0 ) f\left( x \right) = + \infty {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} \left( {x < 0} \right) f(x)=+∞(x<0),所以得知 f ( 1 ) = min { f ( − 1 ) + 1 , f ( − 4 ) + 1 , f ( − 6 ) + 1 } = + ∞ f\left( 1 \right) = \min \left\{ {f\left( { - 1} \right) + 1,f\left( { - 4} \right) + 1,f\left( { - 6} \right) + 1} \right\} = + \infty f(1)=min{ f(−1)+1,f(−4)+1,f(−6)+1}=+∞,这也表示价格1无法由这三枚硬币拼出来。
综上所述,动态规划的递推关系式(或动态规划的基本方程)为:
{ f ( x ) = min { f ( x − 2 ) + 1 , f ( x − 5 ) + 1 , f ( x − 7 ) + 1 } x = 1 , 2 , . . . n f ( 0 ) = 0 f ( x ) = + ∞ ( x < 0 ) \left\{ \begin{array}{l} f\left( x \right) = \min \left\{ {f\left( {x - 2} \right) + 1,f\left( {x - 5} \right) + 1,f\left( {x - 7} \right) + 1} \right\}{\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} x = 1,2,...n\\ f\left( 0 \right) = 0\\ f\left( x \right) = + \infty \left( {x < 0} \right) \end{array} \right. ⎩⎨⎧f(x)=min{ f(x−2)+1,f(x−5)+1,f(x−7)+1}x=1,2,...nf(0)=0f(x)=+∞(x<0)
现在,只要从1开始计算,便可得出结果。(推理时从后往前推,计算时从前往后)
代码:
//动态规划算法,设置price为10000,耗时4ms
public class Coin {
//price代表需要拼凑的价格,0
public int fun(int price){
int[] f = new int[price + 1];//数组下标代表需要拼凑的价格,对应的值表示硬币数量
int[] c ={
2,5,7};//数组c用来存储硬币面值
f[0]=0;//初始条件,
for(int i=1;i<=price;i++){
f[i]= Integer.MAX_VALUE;
for(int j=0;j<c.length;j++){
if(i-c[j]>=0&&f[i-c[j]]!=Integer.MAX_VALUE){
f[i]=Math.min(f[i],f[i-c[j]]+1);
}
}
}
return f[price];
}
}
//暴力破解法,设置price为10000,耗时8812ms
public class Coin {
//0
public int fun(int plaze){
int max = Integer.MAX_VALUE;
for(int i=0;i<=plaze/2;i++){
for(int j=0;j<=(plaze-2*i)/5;j++){
for(int k=0;k<=(plaze-2*i-5*j)/7;k++){
if(2*i+5*j+7*k==plaze){
if(i+j+k<max){
max=i+j+k;
}
}
}
}
}
return max;
}
}
给定m行n列的网络,有一个机器人从左上角 ( 0 , 0 ) \left( {0,0} \right) (0,0)出发,每一步可以向下或者向右走一步。问有多少种不同的方式走到右下角。
解析:
由题可知,无论机器人用何种方式到达右下角(终点),最后挪动的一步要么向右要么向下,设右下角坐标为 ( m , n ) \left( {m,n} \right) (m,n),那么前一步机器人一定在 ( m − 1 , n ) \left( {m - 1,n} \right) (m−1,n)或者 ( m , n − 1 ) \left( {m,n-1} \right) (m,n−1)的位置。
假设机器人有 x x x种方式从起点到达 ( m − 1 , n ) \left( {m - 1,n} \right) (m−1,n),有 y y y种方式从起点到达 ( m , n − 1 ) \left( {m,n-1} \right) (m,n−1),则机器人有 x + y x+y x+y种方式到达终点。
设== f ( i , j ) f\left( {i,j} \right) f(i,j)为机器人有多少种方式从左上角走到 ( i , j ) \left( {i,j} \right) (i,j)==,对于任意一个格子 ( i , j ) \left( {i,j} \right) (i,j),有
f ( i , j ) = f ( i − 1 , j ) + f ( i , j − 1 ) f\left( {i,j} \right) = f\left( {i{\rm{ - 1,j}}} \right) + f\left( {i,j - 1} \right) f(i,j)=f(i−1,j)+f(i,j−1)
初始条件:
f ( 0 , 0 ) = 0 f\left( {0,0} \right) = 0 f(0,0)=0,因为机器人从左上角开始转移
边界条件:
当 i = 0 i=0 i=0或 j = 0 j=0 j=0时,则前一步只能有一个方向过来,即 f ( i , j ) = 1 f\left( {i,j} \right) = 1 f(i,j)=1
先计算第0行,再计算第1行、第2行…第m行。
代码:
public class Path {
public int fun(int m,int n){
int[][] w = new int[m+1][n+1];
w[0][0]=0;//初始条件
for(int i=0;i<=m;i++){
//先行
for(int j=0;j<=n;j++){
//后列
if(i==0||j==0){
//边界条件
w[i][j]=1;
}else{
w[i][j]=w[i-1][j]+w[i][j-1];
}
}
}
return w[m][n];
}
}
参考资料:
30分钟弄懂动态规划算法详细讲解(超详细)
《运筹学 (第三版)》 清华大学出版社