概念篇
线性规划:下图给出了模型
其中目标函数和约束条件里面的不等式函数都是关于xi的线性函数。这类问题都有一些不错的求解方式。
整数规划:若在线性模型中,变量限制为整数,则称为整数线性规划,即为整数规划,可见整数规划狭义上考虑的是线性问题。
然而今天,我们不谈线性规划的这种求最优的问题,之后会专门开辟一个题目来谈,我们今天只谈“风月(闷骚)”。
动态规划:解决多阶段决策问题的一种方法。
其研究对象有:最短路径问题,资源分配(投资)问题,生产计划问题,背包问题,设备更新问题,库存问题......
解决问题的特点:在多阶段决策过程中,解决的动态过程可以按照时间先后分为各个阶段,每个阶段的状态既相互联系又相互区别;每个阶段都要进行决策,决策的目的是使整个过程的决策达到最优效果。最优性原理:最优策略的子策略必为最优。动态规划具有最优性原理这一性质。
遍动态规划算法设计的大致的四个步骤:
- 描述最优解的结构
- 递归定义最优解的值
- 按自底向上的方式计算最优解
- 由计算出的结果构造一个最优解
例题篇
以上为拗脑的概念,相信大家在各种博客里的描述见得不少前篇一律,没啥好看的,但是有一点,例子见得少,这些概念不一定都能明白,老生常谈一下,下面引入各大高校奉为经典的求最短路径的例题。可能是因为它好讲吧,但是美国人著述的算法导论上的开篇例子却不一样。没有例子的理解,我觉着都是干吼般的咆哮,吼过之后还是空虚寂寞加阴冷。
例1: 如下图,求A到F的最短路径的例题:
根据动态规划的一般解题步骤有:
- 我们考虑这样一个命题,如若考虑从A到F的最短路径,那么该路径上各点到U的也是最短路径。
- 设A到F的最短距离为dA,B1点到F的最短距离为dB1,……依次类设,姑有:
dA = min{4+dB1,5+dB2}
dB1 = min{2+dC1,3+dC2,6+dC3}
dB2 = min{7+dC4,8+dC2,7+dC3}
dC1 = min{5+dD1,8+dD2}
……
3. 自底向上的方式计算最优解dA:
dE1 = 4,dE2 = 3,
dD1 = min{3 + dE1,5 + dE2} = 7
dD2 = min{6 + dE1,2 + dE2} = 5 (D2,E2)
dD3 = min{1 + dE1,3 + dE2} = 5
dC1 = min{5 + dD1,8 + dD2} =12
dC2 = min{4 + dD1,5 + dD2} =10 (C2,D2)
dC3 = min{3 + dD2,4 + dD3} =8
dC4 = min{8 + dD2,4 + dD3} =9
dB1 = min{2 + dC1,3 + dC2,6 + dC3} = 13 (B1,C2)
dB2 = min{7 + dC4,8 + dC2,7 + dC3} = 16
dA = min{4+dB1,5+dB2}=17 (A,B1)
4. 构造最优路径如上(A,B1,C2,D2,E2)
例2:把正数a分成n个部分,使其乘积最大。
设 Pn(a) 是 a分成n个部分其乘积的最大值,则:
P1(a) = a
P2(a) = max{x* P1(a-x)}= max{x*(a-x)},0<=x<=a
求导取其极值点x=a/2
故,p2=(a/2)^2
Pn (a) = max{x* Pn-1(a-x)} ,0<=x<=a
用数学归纳法征得,x=a/n, Pn (a)=(a/n)^n
这里的x是连续变量
例3:如下图,灰色长条代表从某时间开始到某时间结束的任务,灰色长条上的红色数字代表完成任务后的报酬,黑色数字代表任务序号,任务不能重叠,求如何选择任务使得酬劳最多?
设OPT(i)为前i个任务的最多报酬,则有如下递推式:
其中prev(i)是选择做当前任务后的前一个不重叠的任务序号,OPT(prev(i)) 为前prev(i)个任务的最多报酬,先来看看prev(i)的所指,有如下:
图中推知,考虑前8个任务和前6个任务的最多报酬时,都有可能需要前5个任务的最多报酬,这样就出现了重叠子问题的现象。
现在针对每一个i,根据递推式来考虑OPT(i)的值,如下图(蓝色是选择的任务序号):
这个例子有个重叠子问题的概念,为了使重叠子问题不重复计算,我们会把重叠子问题的最优解缓存起来,以便处理其他子问题的时候直接使用,不用再次计算。动态规划程序设计上一般不会选择递归的方式。而是用空间来降低时间复杂度。
例4:如图数组,如何选择不相邻的元素,使得加起来和最大?
设OPT(i)为前i个元素加起来的最大和,则有如下图推知OPT(i):(会有重叠子问题,+代表选择该项,-表示不选)
且有有如下递推式:
递归的初值(出口)有:
代码表示有:
1. 递归函数
arr = []
def rec_opt(arr,i){
if i==0:
return arr[0]
elif I == 1:
return max(arr[0],arr[1])
else :
A= rec_opt(arr,i-2)+arr[i]
B= rec_opt(arr,i-1)
return max(A,B)}
rec_opt(arr,6)
}
2. 非递归函数
def dp_opt(arr):
opt=np.zeros(arr.length);
opt[0]=arr[0]
opt[1]= max(arr[0],arr[1])
for I in range(2,len(arr)):
A=opt(arr,i-2)+arr[i]
B=opt(arr,i-1)
Opt[i]=max(A,B)
Return arr[arr.length-1]
dp_opt(arr)
例5:如下一组数:
问:是否存在某些元素有加起来等于S的可能组和?
设 subset(arr[i],S) 表示前i项存在在某些元素有加起来等于S的可能组合,则自底而上推导如下图:
且有有如下递推式:
递归的初值(出口)有:
代码表示有:
1. 递归函数
2. 非递归函数,分析各阶段状态值subset(arr[i],S[j])如下图:
例6:资源配置(投资)问题,设有资源a,分配给n个项目,设gi(xi)为分配资源xi给项目i所得的利润,求如何分配资源使得利润最大化?例如有7万元投资到A,B,C三个项目,其利润如下表:
目标函数: max z = g1(x1)+g2(x2)+ g3(x3)+…+ gn(xn)
约束条件:x1+x2+…+xn=a,xi>=0,i=1,2,3,……
若gi(xi)是xi的线性函数,则是一般的线性规划问题,可以按照线性规划的求解方法求解。无论它是不是线性函数,我们现在用多阶段决策的动态规划来解决。最关键的一步是设。
设fi(x)表示以资源数量x分配给前i个项目所得的最大利润。则问题求解的就是fn(a)对应例子就是求f3(7)
i=1:f1(x)=max{g1(x)},表示以资源数量x分配给第一个项目A所得的最大利润,其中,0<x<=a
1<i<=n:有如下递推式:
fi(x)=max{gi(y)+fi-1(x-y)},其中,y表示资源数x中分配给第i个项目的资源数,x-y表示资源数x中分配给前i-1个项目的资源数,即:0<=y<=x。fi-1(x-y)当然表示资源数x中的x-y个资源分配给前i-1个项目所得的最大利润。以上的资源x,y个资源都指x,y万元的人民币
第一步 考虑f1(x)中的x取值不同,第一个项目A所得的最大利润:
第二步,考虑再加入第二个项目B,f2(x)中的x取值不同,前2个项目A,B所得的最大利润,此时有递推式:f2(x)=max{g2(y)+f1(x-y)} 0<x<=a,0<=y<=x
f2(7)=max={ g2(0)+f1(7),g2(1)+ f1(6),g2(2)+ f1(5),g2(3)+ f1(4),g2(4)+ f1(3) ,g2(5)+ f1(2) ,g2(6)+ f1(1) ,g2(7)+ f1(0)}=max{0.35,0.42,0.4,0.42,0.38,0.38,0.40,0.34}=0.42
同理算得:f2(1)=0.12, f2(2)=0.23,f2(3)=0.27, f2(4)=0.32, f2(5)=0.34, f2(6)=0.37
第三步,考虑再加入第三个项目C,f3(x)中的x取值不同,前3个项目A,B,C所得的最大利润,此时有递推式:f3(x)=max{g3(y)+f2(x-y)} 0<x<=a,0<=y<=x
故有,f3(7)=max{g3(y)+f2(7-y)}=0.52,即7万元分配给三个项目A,B,C所得的最大利润为0.52万,分别投资1,3,3万元
显然,此题也有递归和非递归的2种程序设计。
例7:完全背包问题。背包限重a,有n种物品各若干,其重量和单价已知,问题是如何装使得总价最大?
设单位重量为ai,单价为ci,xi表示装物品i有xi件,i=1,2,3……
目标函数: max z = c1*x1+ c2*x2+c3*x3+…+cn*xn
约束条件: x1*a1+ x2*a2+ x3*a3+…+xn *an <=a,xi>=0且为整数,i=1,2,3,……
若a=5,3中物品的重量和单价如下:
则有:
可以看到这是一个整数规划问题,可以用分支界定法,和割平面法来解决。
以下关注动态规划的解法:
设fi(x)为总重量不超过x,包中只装有前i中物品的最大总价,a>=x>=0,i=1,2,3…则问题的求解变为求fn(a)即:总重量不超过a,包中装有n中物品的最大总价
i=1: f1(x)=max{ c1*y },y*a1<=x,y表示装第1个物品的件数
逐一加入第3,4,…,n件物品来考量不超过x,包中只装有前i中物品的最大总价,就是一个递推的过程,所以有如下递推式:
fi(x)= max{ ci*y+ fi-1(x-y*ai)} , x<=a, y<= floor(x/ai), 1<i<=n
计算过程如下:
f1(x)=c1*floor(x/a1),x<=a
f2(x)= max{ c2*y+ f1(x-y*a2)}, f1(x-y*a2)= c1*floor((x-y*a2)/a1), x<=a, y<= floor(x/a2)
…
fn(x)= max{ cn*y+ fn-1(x-y*an)}, x<=a, y<= floor(x/an)
上例中就是求f3(5)= max{ c3*y + f2(5-y*a3)}, x<=5,0<= y<= floor(5/a3),c3,a3带入其递推式有:f3(5)= max{ 12*y + f2(5-y*5)}, x<=5,0<=y<= floor(5/5)=1,y=0,1
f3(5)=max{f2(5),12+f2(0)}
f2(5)= max{ 5*y + f1(5-y*2)}, y<= floor(5/2)=2,y=0,1,2
故f2(5)=max{f1(5),5+f1(3),10+f1(1)},
而f1(x)=8*floor(x/3),x<=5,y<=floor(x/3) y=0,1
则f1(1)=f1(2)=0, f1(3)=f1(4)=f1(5)=8
故f2(5)=max{8,13,10}=13,
而f2(0)= max{ 5*y + f1(0-y*2)}, y<= floor(0/2)=2,y=0
故f2(0)=f1(0)=0
故f3(5)=max{13,12}=13 即,第3个物品装0个,第2个物品装1个,第1个物品装1个,可装的最大总价13
考虑递归算法:
int[] a = new int[]{3,2,5};//单重
int[] c = new int[]{8,5,12};//单价
Result {
double v; //总重量不超过x,包中只装有前i中物品的最大总价
int num;//第i中物品所装的件数
int i;// 物品序号
}
Result f(double x,int i){
int limit = floor(x/a[i]);
if(i==0){
return new Result(c[i]*limit,limit,0);
}
int y = 0;
double max = t = 0.0d;
while(y<= limit){
if(max < t = c[i]*y+ f (x-y*a[i],i-1).v){
max = t;
}
y++;
}
return new Result(max,num,i);
}
我们可以看到本例的解法还能用于重量是连续实数的情况,即大可不必限制在离散整数范围内,若并本例中的背包重量,单位重量以及单价都是整数的话,那么我们在求解的过程中一定不会出现非整数,为什么?从上面推fi的递推式max{ ci*y + fi-1(x-y*ai)} , x<=a, y<= floor(x/ai)可知,运算的操作有floor,ci*y,x-y*ai,max,这些运算的值都是整数,即fi不可能是非整数。那么,我们就可以所有的fi缓存起来供递推式自调用,所以就有一种非递归的方式来实现这个例子。另外若重量,单位重量以及单价有一样是非整数的话,背包九讲里还有O(an)这个时间复杂度吗?还有非递归的方式吗?
例8 0-1背包,借用上例题干,但每种物品要么不装要么只能装一件,同问在不超过包的负载量a的约束条件下,可以装出的最大总价。
还是设fi(x)为总重量不超过x,包中只装有前i中物品的最大总价。则其递推式有:
fi(x)= max{ fi-1(x),ci+ fi-1(x-ai)} , x<=a, 1<i<=n
f1(x)=c1(x>=a1)or 0(x<a1),
其实本例和例3,例4有些相像,都是在选与不选的全覆盖式条件上做决策。若本例的重量是实数的话,也是不能用非递归实现的。把x=5带入f3(x)自地往上推将上去,就可求得本问题的解。推略。
例9最长公共子序列,解略。
总结篇
好,现在来看一下大师们总结的一些概念,一个是最优子结构,另一个是重叠子问题,这俩概念是动态规划的本质特点。万事开头难,说的就是动态规划的第一步,第一步就是定义最优解的结构,使得解的子结构也最优,这也是重要的一步,例如定义fi(x)为总重量不超过x,包中只装有前i中物品的最大总价,它具有最优子结构,即1<i<=n的每一个i和每一个x组合,都代表一个最优的子结构或者说最优的子问题。和重叠子问题关联的概念还有,状态,状态转移方程。当我们不采用递归的方式设计程序时,那我们的思考的就是让这些最优子问题的值缓存起来,以供让其他的最优子问题调用决策,这些最优子问题的值就是状态,而状态转移方程就是用于决策的递推式,例7中fi(x)取不同的i和不同的x都是某一阶段的状态,至于阶段其实也很好理解,例7中f3(5)这是第一阶段的状态,f3(!5)也可以算作第一阶段,但是f3(!5)这些状态在例7中毫无用处,所以第一阶段就只有f3(5)这一个用到的状态,为了求的最终问题f3(5),根据递推式,需要先求得f2(5),f2(0)这俩状态,这属于第二阶段的状态,我们看出来阶段的分段是根据物品的种类来划分,所以f1(x)属于第三阶段。至此,有了重叠子问题,状态和状态转移方程的理解,我们也就明白了一些文章把第一步定义最优解的结构叫定义状态。
分数背包。例7中若重量,单位重量以及单价有一样是非整数的话,可以称作非整数背包,那我们可以称做分数背包吗?
转载请注明出处
参考:
https://www.bilibili.com/video/av16544031?from=search&seid=16579408987279019650
清华大学计算机工程与科学系的组合数学课本
你的打赏是我奋笔疾书的动力!
支付宝打赏:
微信打赏: