算法设计分析之三(动态规划,最长公共子序列)

1.        动态规划

动态规划是通过组合子问题的解从而得到整个问题的解。

1.1.       与分治算法区别

分治法是将问题分解为独立的子问题,递归求解子问题,最后再合并子问题从而得到问题的解。

动态规划并不要求子问题独立,因此子问题可以包含公共的子子问题。这时候如果用分治法,则会重复计算很多不必要的工作,即重复计算公共的子子问题。

1.2.       思想

动态规划是将子子问题的解存放在一张表中,从而在重复需要的时候不必重复求解,而是直接查表。

1.3.       应用场景

动态规划一般用于最优问题的求解。这样的解为该问题的一个最优解,因为最优解可能不止一个。

1.4.       动态规划设计步骤

Ø  描述最优解结构

Ø  递归定义最优解的值

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

Ø  由计算的值构造一个最优解(构造,最优解怎么取值---每一步。)

在职要求最优解值时,可以不用求解最优解的过程。如果需要构造过程,则在自底向上计算时,需要存储信息,以便记录用于构造最优解的过程。

2.        实例分析

2.1.       装配线调度

有两条装配线,装配站个数相同。相同位置上的装配站所做工作相同。

不同点是每条线上每个装配站完成自己的工作所需时间不同;即便不同装配线相对应的装配站,所作相同工作其时间也不同。

为简单,只考虑两条装配线。

2.1.1.       问题:

一般一个订单只在一条装配线上完成任务;如果来了一个紧急订单,可以在两条装配线上进行调度,以便最快完成任务。最快完成需要多少时间?怎样在两条装配线上流转,以便达到最快?

假设在同一条装配线上的一个装配站,流转到本装配线下一个装配站不需要计算时间;但是流转到另一个装配线的下一个对应的装配站需要计算时间。

定义:

每条装配线都有n个装配站。

装配线i的第j个装配站为Sij(i=1,2;j=1,…,n)。

装配站Sij工作所需的时间为aij(i=1,2;j=1,…,n)。

任务进入到第i条装配线的时间为ei(i=1,2);

任务从最后一个装配站完成后离开装配线的时间为xi(i=1,2)。

任务从第i个装配线的第j个装配站,移动到另一个装配线的下一个装配站(j+1)所需时间为tij(i=1,2;j=1,…,n-1)。最后一个装配站出去后不可以再移动到另一个装配线。

2.1.2.       解法:

蛮力解法:是将所有可能的装配方式计算一次,找出用时最少的。所有装配可能为2^n中装配方式,因此时间为O(2^n),n大了后不可行。

动态规划解法

第一步最优解结构:考虑从起点进入到装配站Sij最快的线路(时间包括aij)。如果j=1,即第一个装配站,则进入线路只有一条,即从开始到Si1。对于j=2,…,n。则进入Sij的最快线路可能为两种情况。一是直接从该装配线的前一个装配站(第j-1装配站)直接进入,则相应的路径也必定是经过Si(j-1)装配站最快的线路;二是从另条装配线的前一个装配站,经过移动操作后移动过来的,则相应的线路必定是经过S(i^)(j-1)(即,经过另一条线路的第j-1个装配线的最快路径)。

因此最优解(Sij的求解)包含了子问题(求解S1(j-1)或S2(j-1))的一个最优解。这种性质称为最优子结构这个性质决定了问题是否可以用动态规划来分析的第一个条件。

第二步递归解:利用子问题的最优解,递归定义问题的最优解。

定义:

fij为在两条装配线上选择装配站时,进入第i条装配线第j个装配站Sij最快的可能时间(包含在该装配站工作的时间aij)。

问题的解是指,任务通过所有线路的最快时间。记f*为问题最优解的最快时间。则最优解最后一步要么经过S1n并离开,要么经过S2n并离开。因此:

f*=min{f1n+x1,f2n+x2}

并且基础情况为:

f11=e1+a11

f21=e2+a21

由第一步最优解结构,对f1n,f2n的求解也可递归求解。从而整个递归关系为:

f1j = min{f1(j-1)+a1j,f2(j-1)+t2j+a1j} (j>=2).基础情况f11如上。

f2j=min{f2(j-1)+a2j,f1(j-1)+t1j+a2j} (j>=2).基础情况f21如上。

由上式即可求解所有的fij,以及f*。而fij即为子问题的最优解。

为了记录最优解的构造

定义:

lij(i=1,2;j=2,…n)为装配线的编号,即值为1或2。其中装配站j-1被通过装配站Sij的最快线路使用。

l11,l21是无意义的,因为没有装配站在第一个装配站的前面。

l*为最优解中,最后一次第n个装配站对应的装配线。

lij的构造和fij的构造是同步的。

最优解的构造是从l*逆向构造出来的。从l*找到最后的n的装配线(如l*=1,则最后从装配线的n装配站离开,即经过S1n),由相应的lin找到li(n-1)(如上需由l1n来计算,如果l1n=1,则倒数第二个也为装配线1上的装配站,即经过S1(n-1);否则如果l1n=2,则倒数第二个为装配线2上的装配站,即经过S2(n-1)),以此反推,直到开头。

第三步计算最优解的值:由递归式计算最优解f*即可。

不好的解法,如果基于递归式的计算有个问题,由于递归求解,低阶的f会被重复计算。而且重复计算的次数将是指数级别的,相当于递归时,栈的展开。

动态规划解法,如果在递归的求解中是从低阶的往高阶的方向求解,且求解的值保存在一个表中,则不会有重复计算f的情况。

在计算的过程中由表格记录下所有的fij,lij。

第四步最优解的构造

方式是由前述所,由l*递归往前求解。打印方式,一按照递减的方式,直接打印即可;二是按递增的方式,需要用递归来求解。

 

2.1.3.      扩展

fij,lij的空间为4*n-2;可以降低空间为2*n+2,且仍然可以求解出最优解的构造。

2.2.       矩阵链乘法(与矩阵分块乘法的分治算法对比)

矩阵链乘法是计算矩阵列表A1A2A3…An,n个矩阵相乘的结果;Ai并不需要为方阵,只要满足矩阵乘法即可。

2.2.1.      问题

由矩阵乘法的结合性,如果计算时结合的顺序不同,则最后计算的代价将是不同的。而且差别有可能是非常大的。如何求解计算量最小的结合方式?

2.2.2.      数学基础

两个矩阵相乘的计算量为:A1为p*q矩阵,A2为q*r,则乘积结构A1A2需要进行的乘法运算量为:p*q*r

2.2.3.      定义

链乘中A1A2…An,矩阵Ai的维数为p(i-1)*pi(i=1,2…,n)。

最优化算法中并不实际计算矩阵的相乘,而只是确定最优化的相乘的顺序。

2.2.4.      解法

蛮力解法:穷尽法,计算所有加括号,进行结合的方式P(n)。

当n=1时,加括号方式为1种,P(n)=1。

当n>=2时,整个乘积结构可以看作两个加括号的子乘积的乘积;而最后一步划分可能发生在第k个和第k+1个矩阵之间(k=1,2,…n-1)。划分方式

P(n)=Sum(P(k)*P(n-k)),下标k=1,2,…n-1。

P(n)的解是Catalan数,增长为(4^n)/(n^(3/2))。从而为指数级别的,因此不可取。

动态规划解法

第一步最优加括号的结构

定义:

记Aij为对AiA(i+1)…Aj的最优解的结果,其中(i<=j)。如果i<j,则其最优解都将在某个k(k=i,..j-1)处即Ak和A(k+1)之间,即最优解为先计算Ai,..Ak,和A(k+1)…Aj,再求这两个结果的乘积。如此,整个Aij的代价即为Aik,A(k+1)j的代价再加上这两个相乘的代价。

最优子结构,如果Aij中,在第k个位置,即Ak和A(k+1)之间的划分将Aij划分为一个最优的,则对Aij的最优的方式中,Aik的最优解即为Aij中最优解的前半部分,且A(k+1)j的最优解,也组成Aij的最优解的后半部分。即Aij的最优解包括Aik,A(k+1)j的最优解。

第二步一个递归解,

mij为Aij中最优解的代价,则整个链相乘的最优解的代价为m1n。

而如果i=j,则为一个矩阵,Aij=Aii。无需进行相乘。因此

mii=0;(i=1,2,…,n)

如果i<j,则由最优解情况,设在第k位置分割,则

mij=mik+m(k+1)j+p(i-1)*pk*pj

当然,这个时候只是假定已知k的位置,但实际还并不知道。但是因为k=i,..j-1,因此最多为计算j-i次,并取最小值即可。从而递归解为:

mij=min{mik+m(k+1)j+p(i-1)*pk*pj} ,k=i,..j-1,i<j.

平凡的i=j时mii=0。

为了记录最优解的构造

定义:

sij为Aij最优解的分裂位置k。即sij为使得递归式中取值最小的k。

第三步计算最优解的值:最优解的值可以由递归式求解。

不好的解法,直接用递归,则低阶的重复计算,代价为指数级别,因此效率不行。

动态规划解法,由于每一对满足1<=i<=j<=n的i,j对对应一个问题,总共有C(n,2)+n=O(n^2)个子问题。而递归算法在递归树中,可能会多次遇到同一个子问题。因此有子问题的重叠子问题的重叠性决定了是否可以用动态规划来解的第二个性质。(第一个为最优子结构。)

求解过程也并不是直接用递归,而是使用表格法。

输入序列p=<p0,p1,…,pn>为矩阵的维数数列。数列长度为n+1。并用mij,sij分别表示ij的代价,以及ij的分裂位置k。并用sij来构造最优解的结构。

实现:为了实现自底向上,因此需要确定哪些表项会用来计算mij。由递归式,计算Aij代价mij时,只依赖于两个个数少于j-i+1个的矩阵的乘积。

做法:首先初始化mii为0;再根据Aij的链表的长度,从2到n进行循环,每次计算出所有长度相同的mij。因此计算mij的时候,只依赖已经计算过的mik和m(k+1)j。

循环包括三层,第一层链长l为2,..n,第二层i为1,..n-l+1,j为i+l-1,第三层k为i,…j,这时候根据已经计算的Aik和A(k+1)j,以及递归式来计算最小的代价mij,以及sij。

mij为对角线为0的一个上三角,并将对角线水平放置,A1..An从左到右进行放置;i为从顶点到对角线,从右边由1增加到n;j为从顶点到对角线,从左边右n减少到1。每次循环计算一行的值,顶点的值即为m1n。中间的值则为mij。

Sij为没有对角线的上三角,比mij少一行,相应的i,j的变化和mij相同。

复杂度,由三层循环,且可以分析这三层的时间复杂度为O(n^3),空间复杂度为O(n^2)。因此比穷举法指数时间高效。

第四步最优解的构造:由sij构造,且由s1n(即s表的顶点),可得最优解最优的一次结合;递归对前后两部分在sij中查询,即可得最终的最优解的构造。

特别:含义n个矩阵的乘积,括号对数为n-1。

2.3.       最长公共子序列(LCS)

应用:找出两条DNA的相似度。找出第三条DNA,其出现在这两条DNA中,且顺序也相同,但可以不连续。

2.3.1.      解法

蛮力法:找出一个链的所有子列,并在另一个链中查找。会有指数级别的复杂度。因此不好。

动态规划解法:

第一步最优子结构

定义:X的第i个前缀为Xi=<x1,x2,…,xi>,i=0,1,..n。并令X0=0。

X=(x1,x2…xm),Y=(y1,y2…yn)。最优解Z(z1,z2…zk)为X,Y的最优解,则

如果xm=yn,则最优解和解X=(x1,..xm-1)与Y=(y1,..ym-1)的最优解一直。

(如果xm于yn不等,则由Z为最优解,则比至为X,Y中一个的元素。)

这时候如果xm与zk不同,则最优解的子解Z(z1,..z(k-1))一定为Xm-1与Yn的一个LCS.

同理,如果yn与zk不同,则最优解的子解Z(z1,..z(k-1))一定为Xm与Y(n-1)的一个LCS.

第二步递归解

定义cij为子序列Xi和Yj的最优解的长度。则

Cij=0;i=0或j=0时。

Cij=c(i-1)(j-1) + 1 ;i>0,j>0且xi=yj。

Cij=max{c[i,j-1],c[i-1][j]};i>0,j>0且xi与yi不相等。

第三步计算最长公共子序列的长度值,使用cij来填入表中,定义b(1..m)(1…n)

第四步构造一个最优解。

 

2.4.       最优二叉查找树(霍夫曼树)

 

3.        动态规划的基础(即应用条件)

3.1.       动态规划的使用需要两个条件:

3.1.1.      一是存在最优子结构(即问题的最优解包括子问题的最优解。具有最优子结构时,只是有可能使用动态规划解决,这时候使用贪心算法也可以),动态规划中使用子问题的最优解来构造整个问题的最优解,因此子问题的最优解必须也是构成整个问题最优解的一部分。

最优子结构的寻找时,有一定的模式:

Ø  问题的最优解为一种选择,在不同的情况下来选择最好的那个。因此可能依赖于多个子问题。

Ø  如果已知一个最优解是需要一个选择,这时候可以不必知道具体的选择,先默认已知选择的值。

Ø  已知选择后,要确定哪些子问题会随之发生,以及如何来描述这些子问题的空间。

Ø  证明最优解的选择中,使用的子问题的解也是子问题的最优先的。

子问题数和每个子问题可能的选择数是不同的概念。且总的复杂度会依赖于这两个数。

应用比较:

最短路径和最长路径。

最短路径(u到v)有最优解子结构。假设中间点为w,则最短路径可以分解为子问题(u,w)和(w,v),且也为最短路径。从而存在最优解子结构。

最长路径(u到v)无最优解子结构。(且为NP问题)因为最长路径中,由中间点w划分的路径后,子问题(u,w)和(w,v)不一定是最长路径。原因在于这时候的子问题不独立,独立的含义是:一个子问题的解不会影响另一个子问题的解,即子问题不会共享资源,从而在合并时出现问题。

3.1.2.      重叠子问题

子问题的空间要“很小”,即用于解问题的子问题要可以重复利用,而不是会产生新的子问题。也即,不同的子问题数为输入规模的多项式级别。

特别要注意,分治法一般会产生新问题。

3.1.3.      最优解的构造,备忘录法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值