Dynamic Programming(动态规划)

参考文献:算法导论


可以使用动态规划的两个充分条件:

1.最优子结构(一个问题的最优解中包含了子问题的最优解,也可以适用贪心策略)

2.重叠子问题(一个递归树在不同的分支中可能碰到相同的子问题)


DP步骤:

1.描述最优解的结构

2.递归定义最优解的值

3.按自底向上的方式计算最优解的值

4.由计算出的结果构造一个最优解


通过工厂最快路线问题

题设:工厂有2调装配线,每条装配线都有n个装配步骤,两条装配线在执行同一步骤的时间是不同的,同一装配线由上一步骤转移到下一步骤的时间可以忽略,而有一条装配线转移到另一条需要一定的时间



我们可以令fi[j]是我们执行第i条装配线执行第j步的最短时间。

那么此时针对题设,只有两种情况,要么从装配线2转移到装配线1,要么直接从装配线1的上一步转到下一步。


如果是第1条生产线,那么我们很容易会写出一个递归式:f1[j] = min{ f1[j-1]+a1j, f2[j-1]+t2[j-1]+a1j }(t2[j-1]是从第2条装配线的第j-1步移到第一条装配线的第j步所花费的时间,a1[j]是第1条生产线执行第j步的时间)

f1[j-1]+a1j表示从装配线1的j-1步转到j步,此时花费的总时间

f2[j-1]+a1j+t2[j-1]表示从装配线2的j-1步转移到装配线1执行第j步,此时花费的总时间


如果我们使用递归计算,我们可以令调用fi[j]的次数为ri[j]

那么由递归式子,容易得出r1[j] = r2[j] = r1[j+1] + r2[j+1]

如果一条装配线由n步,那么可以得出最初的j11被调用了2^(n-1)次(我们可以建立一个树形模型来讨论递归问题,递归树在《算法导论》第一章有讲过,不是这篇文章的重点),整个算法的时间复杂度是O(2^n)这是无法忍受的低效率。


我们可以简化理解一下递归式,每次计算当前步骤的时间都会用到上一次所计算的时间,那么我们不用每次都从头开始计算,算法可以并行的解每次每条装配线的最优解,那么可以将算法优化为时间复杂度仅为O(n)

算法伪代码

FASTEST-WAY(a, t, e, x, n)

f1[1] <- e1 + a(1,1)
f2[1] <- e2 + a(2,1)
for j <- 2 to n
    do if f1[j-1] + a(1,j) <= f2[j-1]+t(2,j-1)+a(1,j)
            then f1[j] <- f1[j-1]+a(1,j)
                l1[j] <- 1
            else f1[j] <- f2[j-1]+t(2,j-1)+a(1,j)
                l1[j] <- 2
        if f2[j-1]+a(2,j) <= f1[j-1]+t(1,j-1)+a(2,j)
            then f2[j] <- f2[j-1] + a(2,j)
                l2[j] <- 2
            else f2[j] <- f1[j-1]+t(1,j-1)+a(2,j)
                l2[j] <- 1
if f1[n]+x1 <= f2[n] + x2
    then f* <- f1[n] + x1
        l* <- 1
    else f* <- f2[n] + x2
        l* <- 2


矩阵链乘法

题设:给定由n个要相乘的矩阵构成的链<A1 , A2 , A3 , ... , An>
由于矩阵需要相容相乘,例如A1是个i*j的矩阵,A2是j*q的矩阵,则A1*A2的计算次数为i*j*q,
考虑3个矩阵链乘:A1:10*100 , A2:100*5 , A3:5*50
有2种情况:((A1*A2)*A3),(A1*(A2*A3))
第一种情况:10*100*5+10*5*50 = 7500
第二种情况:100*5*50 + 10*100*50 = 75000
则第一种情况是第二种情况计算次数的1/10;我们以标量运算次数来衡量时间,则可以简单理解为第二种情况要比第一种情况慢10倍
我们希望在计算之前找到一种加括号的方式,使得计算次数最少

令一个包含n个矩阵的链所有加括号的可能方案为Pn,我们可以考虑其实该链可以分裂成两个加括号的子链,其分裂位置为k,k可以在第1~n-1个矩阵之后,所以可以写出一个递归式

Pn = ∑P(k)*P(n-k) (k = 1~n-1)

那么我们用递归树的方式来分解这个加括号方案,可以理解为一个压栈和弹栈的过程,压栈次数必须大于弹栈次数,是一个Catalan数序列(Catalan数,我理解为一个古典概率问题,证明有代数和几何方式,个人感觉几何的折现法比较易懂,大家有兴趣可以看看),其解为C(2n,n)/n+1,解该递归式子的时间复杂度为O(2^n)。


我们按照DP四个步骤来分析:

最优解的结构

我们考虑Ai*A(i+1)*...*Aj的矩阵链乘法(记为Ai...j),如果我们从k位置分裂矩阵链,则Ai..k和Ak+1...j,必须是最优的解,因为如果有更优的解,那么更优解必定会替换当前解。

递归定义

令m[i,j]为Ai..j的最优解,则
如果i = j, m[i,j] = 0;
如果i < j, m[i,j] = min{ m[i,k]+m[k+1,j]+p(i-1)pkpj};(k=i...j-1, pi-1*pi表示第i个矩阵的维度)

计算最优解的值

MATRIX-CHAIN-ORDER(p)

n <- length[p]-1
for i <- 1 to n
    do m[i,i] <- 0
for l <- 2 to n
    do for i <- 1 to n-l+1
        do j <- i+l -1
            m[i,j] <- ∞
            for k <- i to j-1
                do q <- m[i,k]+m[k+1,j]+p(i-1)p(k)p(j)
                    if q<m[i,j]
                        then m[i,j] <- q
                            s[i,j] <- k
return m and s

例如:

计算下属矩阵的最小标量计算次数

A1  30*35

A2  35*15

A3  15*5

A4  5*10

A5  10*20

A6  20*25

第一步:计算1-2,2-3,3-4,4-5,5-6的计算次数

m:

 123456
1030*35*15    
2 035*15*5   
3  015*5*10  
4   05*10*20 
5    010*20*25
6     0

s:

 123456
101    
2 02   
3  03  
4   04 
5    05
6     0

计算过程:

A1*A2 = m[1,1] + m[2,2] + p0p1p2 = 30*35*15 = 15750


第二步:计算1-3,2-4,3-5,4-6

m:

 123456
10157507875   
2 026254375  
3  07502500 
4   010003500
5    05000
6     0

s:

 123456
1011   
2 023  
3  033 
4   045
5    05
6     0

计算过程

例如1-3:

(A1*A2)*A3 = m[1,2] + m[3,3] + p0p2p3 = 30*35*15 + 30*15*5 = 18000

A1*(A2*A3) = m[1,1] + m[2,3] + p0p1p3 = 35*15*5 + 30*35*5 = 7875

7875 < 18000

所以A1...3加括号方式为A1*(A2*A3),计算结果为7875


以此类推,最终计算结果

m:

 123456
1015750787593751187515125
2 026254375712510500
3  075025005375
4   010003500
5    05000
6     0

s:

 123456
1011333
2 02333
3  0333
4   045
5    05
6     0


构造一个最优解

由m表可知:A1...6至少要执行15125次标量计算

由s表得出加括号的结果:

A1...6在3,4之间拆分

A1...3在1,2之间拆分

A4...6在5,6之间拆分

我们可以绘制一颗树来表示括号结构

                  A1...6

                /          \

       A1...3            A4...6

      /        \            /       \

  A1     A2...3    A4        A5...6

           /       \                /        \

        A2      A3          A5        A6

那么可以得出括号结构为:((A1*(A2*A3))*(A4*(A5*A6)))

由m表可知:A1...6至少要执行15125次标量计算

由s表得出加括号的结果:

A1...6在3,4之间拆分

A1...3在1,2之间拆分

A4...6在5,6之间拆分

我们可以绘制一颗树来表示括号结构

                  A1...6

                /          \

       A1...3            A4...6

      /        \            /       \

  A1     A2...3    A4        A5...6

           /       \                /        \

        A2      A3          A5        A6

那么可以得出括号结构为:((A1*(A2*A3))*(A4*(A5*A6)))


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值