440 likes | 582 Views
第四章 动态规划. 矩阵连乘问题. 给定 n 个矩阵: A 1 , A 2 , …, A n ,其中 A i 与 A i+1 是可乘的。确定一种连乘的顺序,使得矩阵连乘的计算量为最小。 设 A 和 B 分别是 p×q 和 q×r 的两个矩阵,则乘积 C=AB 为 p×r 的矩阵,计算量为 pqr 次数乘。 但是对于多于 2 个以上的矩阵连乘,连乘的顺序却非常重要,因为不同的顺序的总计算量将会有很大的差别。. 不同计算顺序的差别. 设矩阵 A 1 , A 2 和 A 3 分别为 10×100, 100×5 和 5×50 的矩阵,现要计算 A 1 A 2 A 3 。
E N D
第四章动态规划 算法设计与分析
矩阵连乘问题 • 给定n个矩阵:A1, A2, …, An,其中Ai与Ai+1是可乘的。确定一种连乘的顺序,使得矩阵连乘的计算量为最小。 • 设A和B分别是p×q和q×r的两个矩阵,则乘积C=AB为p×r的矩阵,计算量为pqr次数乘。 • 但是对于多于2个以上的矩阵连乘,连乘的顺序却非常重要,因为不同的顺序的总计算量将会有很大的差别。 算法设计与分析
不同计算顺序的差别 • 设矩阵A1, A2和A3分别为10×100, 100×5和5×50的矩阵,现要计算A1A2A3 。 • 若按((A1A2)A3)来计算,则需要的数乘次数为10×100×5 + 10×5×50 = 7500 • 若按(A1(A2 A3))来计算,则需要的数乘次数为100 ×5 ×50+ 10×100×50 = 75000 • 后一种计算顺序的计算量竟是前者的10倍! • 所以,求多个矩阵的连乘积时,计算的结合顺序是十分重要的。 算法设计与分析
不同计算顺序的数量 • 设n个矩阵的连乘积有P(n)个不同的计算顺序。 • 先在第k个和第k+1个矩阵之间将原矩阵序列分成两个矩阵子序列,k=1,…,n;再分别对两个子序列完全加括号,最后对结果加括号,便得到原序列的一种完全加括号方式。 • 由此可得出关于P(n)的递归式: • n = 1 • n–1 • ∑P(k) P(n–k) n>1 • k=1 P(n) = • 解此递归方程可得P(n) = C(n–1),其中 1 n+1 2n n C(n) = = Ω(4n/n3/2) • 所以P(n)随n的增长呈指数增长。因而穷举搜索法不是有效的算法。 算法设计与分析
分解最优解的结构 • 将矩阵连乘积AiAi+1…Aj记为A[i: j]。 • 若A[1: n] 的一个最优解是在矩阵Ak和Ak+1处断开的,即A[1: n] = (A[1: k] A[k+1: n]),则A[1: k]和A[k+1: n]也分别是最优解。 • 事实上,若A[1: k]的一个计算次序所需计算量更少的话,则用此计算次序替换原来的次序,则得到A[1: n]一个更少的计算量,这是一个矛盾。同理A[k+1: n]也是最优解。 • 最优子结构性质:最优解的子结构也最优的。 算法设计与分析
建立递归关系 • 令m[i][j] , 1≤i, j≤n,为计算A[i, j] 的最少数乘次数,则原问题为m[1][n]。 • 当i = j时,A[i, j]为单一矩阵, m[i][j] = 0; • 当i<j时,利用最优子结构性质有: min{m[i][k] + m[k+1][j] + pi–1pkpj} i≤k<j m[i][j] = 其中矩阵Ai ,1≤i≤n,的维数为pi–1×pi。 • 根据此递归式就可以直接用递归程序来实现。 算法设计与分析
直接递归的时间复杂性 • 根据前面的递归式不难得出的时间复杂性为 n–1 1 + ∑(T(k) + T(n–k) + 1) k=1 T(n) ≥ n–1 = 1 + (n – 1) +∑(T(k) + T(n–k)) k=1 n–1 n–1 = n +∑T(k) + ∑T(n–k) k=1 k=1 n–1 = n + 2∑T(k) k=1 • 可用数学归纳法证明T(n)≥2n–1 = Ω(2n)。 • 直接递归算法的时间复杂性随n的指数增长。 算法设计与分析
直接递归中有大量重复计算 • 直接递归中有大量重复计算,如A[1: 4]计算中: 图中红框标出的都是重复计算。 1: 4 1: 2 3: 4 1: 1 2: 4 1: 3 4: 4 1:1 2: 2 3: 3 4: 4 1: 1 2: 3 1: 2 3: 3 2: 2 3: 4 2: 3 4: 4 3: 3 4: 4 2: 2 3: 3 2: 2 3: 3 1: 1 2: 2 算法设计与分析
消除重复的计算 • 要消除重复计算,可在在计算过程中保存已解决的子问题的答案。这样,每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免重复计算。 • 计算方式可依据递归式自底向上地进行。 • 注意到在此问题中,不同的有序对 (i, j)就对应不同的子问题,因此不同的子问题个数个数最多只有Cn2+ n = (n2)个。 • 这样便可以得到多项式时间的算法。 算法设计与分析
例如: m[1][3] = min m[1][1]+m[2][3]+p0p1p3 m[1][2]+m[3][3]+p0p2p3 自底向上的计算 • 例如对于A1A2A3A4,依据递归式以自底向上的方式计算出各个子问题,其过程如下: 其中 m[2][4] m[i][j] = mini≤k<j {m[i][k]+m[k+1][j]+pi–1pkpj} m[1][3] m[2][4] m[1][2] m[3][4] m[i][i+1] = pi–1pipi+1 m[2][3] m[i][i] = 0 m[1][1] m[3][3] m[4][4] m[2][2] 算法设计与分析
消除重复的矩阵连乘算法 • Void MatrixChain(int p, int n, int **m, int **s) • { for (int i = 1; i <= n; i++) m[i][i] = 0; • //将对角线元素赋值为零,即单个矩阵计算量为0 • for (int r = 2; r <= n; r++) • for (int i = 1; i <= n – r +1; i++) { • int j = i + r – 1; • (5) m[i][j] = m[i+1][j] + p[i–1]*p[i]*p[j]; • //计算A[i, j] = A[i: i] A[i+1: j] • s[i][j] = i; //记下断点i • (7) for (int k = i + 1; k < j; k++) { • int t = m[i][k] + m[k+1][j] + p[i–1]*p[k]*p[j]; • //对i<k<j, 逐个计算A[i, j] = A[i: k] A[k+1: j] • if (t < m[i][j]) {m[i][j] = t; s[i][j] = k;} • //记下较小的m[i][j]及相应的断点k • }}} 能。此处分开是为 了给m[i][j]赋初值。 第(5)步与第(7)步 能否合在一起? 算法设计与分析
MatrixChain的运行举例 MatrixChain用矩阵m[i][j], s[i][j]存放子问题A[i: j]的最小计算量以及相应的断点。 设要计算矩阵连乘积A1A2A3A4A5A6,其维数分别为35×35, 35×15, 15×5, 5×10, 10×20, 20×25, 即p0=35, p1=35, p2=15, p3=5, p4=10, p5=20, p6=25。 当r=2,执行第(5)句完成了相邻矩阵A[i][i+1]=p[i–1]*p[i]*p[j] 的计算,并在s[i][j]中添入了相应的断点。其中的第(7)句因为j = i+1(k=i+1)而被跳过去了,实际并没有执行。 MatrixChain将如下面红色箭头所示的过程逐个计算子问题A[i: j]: 当r=3,i=1时,执行第(5)句计算A[1:1][2:3], m[1][3] = m[2][3] + p[0]*p[1]*p[3] = 2625 +30*35*5 = 7875 当r=3,i=2时,执行第(5)句计算A[2:2][3:4],m[2][4] = m[3][4] + p[1]*p[2]*p[4] = 750 +35*15*10 = 6000 执行for (int i = 1; i <= n; i++) m[i][i] = 0后将对角线元素全部置零,即子问题A[i][i] = 0。 当r=3,i=3时,执行第(5)句计算A[3:3][4:5],m[3][5] = m[4][5] + p[2]*p[3]*p[5] = 1000 +15*5*20 = 2500 类似的,当r=4、5、6时,可以计算出相应的m[i][j]及其相应的断点s[i][j],如下图中所示: 由m[1][6]=15125可知这6个矩阵连乘积的最小运算次数为15125。由s[1][6] = 3可知A[1: 6]的最优计算次序为A[1: 3] A[4: 6];由s[1][3]=1可知A[1: 3]的最优计算次序为A[1: 1] A[2: 3];由s[4][6]=5可知A[4: 6]的最优计算次序为A[4: 5] A[6: 6];因此最优计算次序为:(A1(A2A3))((A4A5)A6)。 当r=3,i=4时,执行第(5)句计算A[4:4][5:6],m[4][6] = m[5][6] + p[3]*p[4]*p[6] = 5000 +5*10*25 = 6250 执行第(7)句计算A[2:3][4:4], t = m[2][3]+m[4][4]+ p[1]*p[3]*p[4] = 2625+0+35*5*10 = 4375<6000,所以m[2][4]改为4375,断点改为3。 执行第(7)句计算A[3:4][5:5], t = m[3][4]+m[5][5]+ p[2]*p[4]*p[5] = 750+0+15*10*20 = 3750>2500,所以m[3][5]仍为2500,断点仍为3。 执行第(7)句计算A[4:5][6:6], t = m[4][5]+m[6][6]+ p[3]*p[5]*p[6] = 1000+0+5*20*25 = 3500<6250,所以m[4][6]改为3500,断点改为5。 执行第(7)句计算A[1:2][3:3], t = m[1][2] + m[3][3] + p[0]*p[2]*p[3] = 15750+0+35*15*5 = 18375>7875,所以m[1][3]不变,断点仍为1。 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 15125 11875 15750 9375 0 7875 1 2 3 4 5 6 3 3 1 1 3 4375 7125 10500 0 2625 6000 3 3 2 3 2 2500 750 5375 0 3 3 3 m[i][j] s[i][j] 3500 6250 1000 5 0 4 4 0 5000 5 0 算法设计与分析
MatrixChain的时间复杂性 • 算法MatrixChain的主要计算取决于程序中对r、i和k的三重循环。循环体内的计算量为O(1),1≤ r、i、k≤n,三重循环的总次数为O(n3)。因此该算法时间复杂性的上界为O(n3),比直接递归算法要有效得多。算法使用空间显然为O(n2)。 • 这种算法称为动态规划。 算法设计与分析
动态规划的基本思想 • 将原问题分解为若干个子问题,先求子问题的解,然后从这些子问题的解得到原问题的解。 • 这些子问题的解往往不是相互独立的。在求解的过程中,许多子问题的解被反复地使用。为了避免重复计算,动态规划算法采用了填表来保存子问题解的方法。 • 在算法中用表格来保存已经求解的子问题的解,无论它是否会被用到。当以后遇到该子问题时即可查表取出其解,避免了重复计算。 算法设计与分析
动态规划的基本要素 • 最优子结构:问题的最优解是由其子问题的最优解所构成的。 • 重叠子问题:子问题之间并非相互独立的,而是彼此有重叠的。 最优子结构性质使我们能够以自底向上的方式递归地从子结构的最优解构造出问题的最优解。 因为子问题重叠,所以存在着重复计算。这样就可以用填表保存子问题解的方法来提高效率。 算法设计与分析
动态规划的基本方法 • 动态规划通常可以按以下几个步骤进行: • (1)找出最优解的性质,并刻画其结构特征; • (2)递归地定义最优值; • (3)以自底向上的方式计算出各子结构的最优值并添入表格中保存; • (4)根据计算最优值时得到的信息,构造最优解。 • 步骤1~3是动态规划算法的基本步骤。若需要最优解,则必须执行第4步,为此还需要在第3步中记录构造最优解所必需的信息。 算法设计与分析
动态规划的备忘录方法 • 动态规划中采用自底向上的方式。但是在递归定义中往往是自上而下的描述。备忘录方法就采用与递归定义一致的自上而下的方式。 • 备忘录方法同样用表格来保存已解子问题的信息。每个子问题初始化时都标记为尚未求解。在递归求解过程中,对每个待解子问题,先查看它是否已求解。若未求解,则计算其解并填表保存。若已求解,则查表取出相应的结果。 • 备忘录方法同样避免了子问题的重复计算,因而和动态规划算法具有同样效率。 算法设计与分析
凸多边形最优三角剖分 • 凸多边形的最优三角剖分问题:给定凸多边形P={v0, v1,…, vn–1} ,以及定义在由凸多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的一个三角剖分,使得该剖分中诸三角形上权之和为最小。 • 可定义三角形上各种各样的权函数w。 • 例如w(vivjvk) = |vivj| + |vjvk| + |vkvi|,其中|vivj|是点vi到vj的欧氏距离。相应于此权函数的最优三角剖分即为最小弦长三角剖分。 • 凸多边形的三角剖分是将一个凸多边形分割成互不相交的三角形的弦的集合T。 • 在凸多边形的一个三角剖分T中,各弦互不相交,且T已达到最大,即任何一条不在T中的弦必与T中某弦相交。 • 下面是一个凸7边形的2个不同的三角剖分: v0 v0 v1 v1 v6 v6 • 在有n个顶点的凸多边形的中三角剖分中,恰有n–3条弦和n–2个三角形。 v2 v2 v5 v5 v3 v3 v4 v4 算法设计与分析
三角剖分与矩阵连乘积同构 • 三角剖分问题和矩阵连乘积问题都对应于一个完全二叉树,也称为表达式的语法树。 • n个矩阵连乘积计算顺序同构于n个叶子的完全二叉树,凸(n+1)边形三角剖分同构于n个叶子的完全二叉树,所以n个矩阵连乘积的计算顺序问题同构于凸(n+1)边形的三角剖分问题。 v0 0 v1 v6 A1 0 1 2 A2 1 A6 3 2 4 v2 A1 3 A4 v5 4 A3 A5 • 事实上,矩阵连乘积最优计算顺序问题相当于是凸多边形最优三角剖分问题中一种特殊定义的权函数的情形。 v3 A4 v4 A2 A3 A5 A6 凸多边形v0v1v2v3v4v5v6一个三角剖分所对应的二叉树 ((A1(A2A3))(A4(A5A6))所对应的二叉树 算法设计与分析
最优子结构性质 • 能应用动态规划求解的问题应具有最优子结构性质。凸多边形最优三角剖分问题具有最优子结构性质。 • 事实上,若凸(n+1)边形P={v0, v1,…, vn}的一个最优三角剖分T包含了三角形v0vkvn,1≤k<n,则T的权为三角形v0vkvn,多边形{v0, v1,…, vk} 和多边形{vk, vk+1,…, vn}的权之和。显然这两个子多边形的三角剖分也是最优的。 • 因为,其中若有一个子多边形的三角剖分不是最优的将导致T不是最优三角剖分的矛盾。 算法设计与分析
最优三角剖分的递归结构 • 定义t[i][j], 1≤i<j≤n, 为子多边形{vi–1, vi,…, vj}的最优三角剖分所对应的权函数值,并为方便起见,设退化的多边形{vi–1, vi}的权值为0。 • 于是凸(n+1)边形的最优三角剖分为t[1][n] • 易知,t[i][j]可递归定义为 • 当i = j时,为退化多边形{vi–1, vi},t[i][j] = 0; • 当i<j时,利用最优子结构性质有: min{t[i][k] + t[k+1][j] + w(vi–1vkvj)} i≤k<j t[i][j] = • 显然,矩阵连乘积的最优计算顺序与凸多边形最优三角剖分具有完全相同的递归结构。 与矩阵连乘积问题相对比: min{m[i][k] + m[k+1][j] + pi–1pkpj} i≤k<j m[i][j] = 算法设计与分析
最优三角剖分的算法 • Void MinWeightTriangulation(int n, Type **t, int **s) • { for (int i = 1; i <= n; i++) t[i][i] = 0; • for (int r = 2; r <= n; r++) • for (int i = 1; i <= n – r +1; i++) { • int j = i + r – 1; • t[i][j] =t[i+1][j] + w(i–1, i, j); • s[i][j] = i; • for (int k = i + 1; k < j; k++) { • int u = t[i][k] + t[k+1][j] + w(i–1, k, j); • if (u < t[i][j]) {t[i][j] = u; s[i][j] = k;} • }}} //程序中红色的部分为改动的地方 • 由于凸多边形最优三角剖分与矩阵连乘积的最优计算顺序具有完全相同的递归结构,其区别仅在于权函数的不同,所以只需要修改求矩阵连乘积的最优计算顺序的算法中的权函数计算便可得到凸多边形最优三角剖分的算法。 • 显然该算法的时间复杂性也是O(n3)。 算法设计与分析
多边形游戏 • 有一个n边形,每个顶点被赋予一整数值,每条边上被赋予一个运算符“+”或“*”。 • 游戏的第1步,将一条边删去; • 随后的n–1步按以下方式操作: • (1)选择一条边E及其所连接的两个顶点v1和v2; • (2)用一个新顶点取代边E以及顶点v1和v2,赋予新顶点的值为顶点v1和v2的值通过边E上的运算所得到的值。 • 最后所有的边被删去,所剩顶点的值即为得分。 算法设计与分析
多边形游戏的表达 • 设所有的边依次从1到n编号,按顺时针序列为 op[1], v[1], op[2], v[2], …, op[n], v[n] 其中op[i]为边i上的运算符,v[i]顶点i上的值。 • 将多边形中始于顶点i (1≤i≤n),长度为j 的顺时针链v[i], op[i+1], …, v[i+j–1]表示为p(i, j)。 • 若链p(i, j)最后一次合并在op[i+s](1≤s≤j–1)处发生,则被分割为两个子链p(i, s)和p(i+s, j–s) 算法设计与分析
多边形游戏的最优子结构性质 • 若链p(i, j)取最优值的最后一次合并是在op[i+s]处发生的,则子链p(i, s)和p(i+s, j–s)也是最优。 • 因为若子链p(i, s)和p(i+s, j–s)不是最优的,则可推出链p(i, j)也不是最优的矛盾。所以多边形游戏具有最优子结构性质。 • 但是这里的最优子结构要稍微地复杂一点。 • 若p(i, j)的最后合并的边op[i+s] =“+”,子链p(i, s)和p(i+s, s)应该取最大值, • 若p(i, j)的最后合并的边op[i+s] =“*”,子链p(i, s)和p(i+s, j–s)是否仍然取最大值呢? 不见得! 算法设计与分析
多边形游戏的最优子结构分析 • 设m1是对子链p(i, s)的任意合并方式得到的值,a和b是其最小值和最大值,m2是对子链p(i+s, j–s)的任意合并方式得到的值,c和d分别是最小值和最大值,即a≤m1≤b, c≤m2≤d。 • 若op[i+s] =“+”,则a+c≤m≤b+d。可见p(i, s)的最小、最大值分别对应于子链的最小、最大值。 • 若op[i+s] =“*”,由于v[i]可取负整数,所以 min{ac, ad, bc, bd}≤m≤max{ac, ad, bc, bd}。 • 即主链最小、最大值亦来自子链最小、最大值。 算法设计与分析
多边形游戏的递归求解 • 由前面的分析可知,为了求链合并的最大值,必须同时求子链合并的最大值和最小值。因此整个计算过程中应同时计算最大值和最小值。 • 设链p(i, j)合并的最大、最小值分别是m[i, j, 0]和m[i, j, 1],最优合并处是在op[i+s] ,且长度小于j的子链的最大、最小值均已算出,且记: • a = m[i, i+s, 0], b = m[i, i+s, 1], c = m[i+s, j–s, 0], d = m[i+s, j–s, 1],则 • ⑴当op[i+s] = “+”时, m[i, j, 0] = a + c,m[i, j, 1] = b + d • ⑵当op[i+s] = “*”时, m[i, j, 0] = min{ac, ad, bc, bd} m[i, j, 1] = max{ac, ad, bc, bd} 算法设计与分析
多边形游戏的递归求解 • 令p(i, j)在op[i+s]断开的最大值和最小值分别为maxf(i, j, s)和minf(i, j, s),综合上面的讨论则 a + c op[i+s] = “+” min{ac, ad, bc, bd} op[i+s] = “*” minf(i, j, s) = b + d op[i+s] = “+” max{ac, ad, bc, bd} op[i+s] = “*” maxf(i, j, s) = • 由于p(i, j)的最优断开位置s有1≤s≤j–1的j–1中情况,于是便可得到求解多边形游戏的递归式: 算法设计与分析
求解多边形游戏的递归式 m[i, j, 0] = min{minf (i, j, s) }, 1≤i, j≤n 1≤s≤j m[i, j, 1] = max{maxf (i, j, s) }, 1≤i, j≤n 1≤s≤j 初始边界值为: m[i, 1, 0] = m[i, 1, 1] = v[i], 1≤i≤n, j = 1 • 由于多边形是封闭的,当i+s>n时,顶点i+s实际编号应为(i+s)mod n。 • 多边形游戏的最大得分即为m[i, n, 1]。 算法设计与分析
多边形游戏的动态规划算法 • 依据上述讨论以及所得的递归式,不难写出多边形游戏动态规划算法。请同学们自己编写。 • 算法的主程序为Poly_Max,它包含了一个子程序MIN_MAX。子程序MIN_MAX负责对确定s的minf (i, j, s)和maxf (i, j, s)的计算。而对任意链的最大值m[i, j, 1]和最小值m[i, j, 0]的计算则是在主程序Poly_Max中进行的。 • 算法的时间复杂性显然仍为O(n3)。 算法设计与分析
DNA序列的相似性 • 将序列A、B表示为对偶(a, b)的序列。其中若: • 1、aA和bB称为替换,a = b称为匹配,否则称为不匹配,其得分为σ (a, b); • 2、aA,b是空位为删除空白; • 3、a是空位,bB为插入空白; • 对于长度为k的空位,其得分为–(q + k r)。 • 这里,参数σ (a, b)、q和r由用户确定。 • 两个序列A、B的相似性为所有这样的对偶序列中最高的得分。 算法设计与分析
DNA序列的相似性 • 若给定两个DNA序列: AGCTACGTACACTACC AGCTATCGTACTAGC • 下面是其一个相似性序列: AGCTA–CGTACACTACC AGCTATCGTAC– –TAGC • 若:a = b时 σ (a, b) = 10; a b时σ (a, b) = –20; q = 40;r = 2。该相似性序列的得分为 10 * 13 – 20 – (40 + 2) – (40 + 2 * 2) = 24 • 其中有13个匹配、1个不匹配、一个长度为1的插入空白和一个长度为2的删除空白。 算法设计与分析
递归求序列相似性 • 设A = a1a2…am,B = b1b2…bn。令S(m, n)为A和B的相似性,即各种对偶序列最大的得分。 • 为了采用递归方法来求解此问题,我们从序列尾部来考虑,那么有三种方案: • ①尾部用替换,即最后一个对偶为(am, bn); • ②尾部用删除空白,即最后一个对偶为(am, –); • ③尾部用插入空白,即最后一个对偶为(–, bn)。 • 令S(m, n)、D(m, n)和I(m, n)分别为三种方案的得分,则其相似性应为三种方案的最大得分。 算法设计与分析
若用方案①,即尾部用替换,则 • 若用方案②,即尾部用删除空白,则 • 若用方案③,即尾部用插入空白,则 S(m, n) = S(m – 1, n – 1) + σ(am, bn)。 D(m, n) = S(m – 1, n) – q – r 或者,D(m, n) = D(m – 1, n) – r I(m, n) = S(m, n – 1) – q – r 或者,I(m, n) = I(m, n – 1) – r 递归求序列相似性 算法设计与分析
对方案①,有 • 对方案②,有 • 对方案③,有 S(0, 0) = 0、S(m, 0) = D(m, 0)、S(0, n) = I(0, n)。 D(m, 0) = D(m – 1, 0) – r、D(0, n) = S(0, n) – q。 I(m, 0) = S(m, 0) – q、I(0, n) = S(0, n – 1) – q。 递归求序列相似性 • 现在考虑非递归分支: 算法设计与分析
0 m = 0, n = 0 D(m, 0) for n 0 I(0, n) for m 0 max{S(m–1,n–1)+σ(am,bn), D(m,n), I(m,n)} • S(m,n) = S(0, n) – q for n 0 D(m – 1, 0) – r for m 0 max{S(m–1,n) – q - r, D(m – 1,n) – r} S(m, 0) – q for m 0 I(0, n – 1) – r for n 0 max{S(m,n–1) – q - r, I(m – 1,n) – r} • D(m,n) = • I(m,n) = 递归求序列相似性 算法设计与分析
动态规划求序列相似性 • 如果用递归算法求序列的相似性,其时间复杂性显然是指数级的。 • 考虑采用动态规划法。为此,需要 • ⑴用矩阵S[0:m, 0:n]、D[0:m, 0:n]和I[0:m, 0: n]来分别记载对应于子序列Ai和Bj的得分S(i, j)、D(i, j)和I(i, j),0 i m,0 j n。 • ⑵从(0, 0)开始逐个计算S(i, j)、D(i, j)和I(i, j)并将结果记入S[i, j]、D[i, j]和I[i, j]。 • ⑶必要时,是用辅助矩阵记载每步的方案。 算法设计与分析
0 m = 0, n = 0 D(i, 0) for j 0 I(0, j) for i 0 max{S(i–1, j–1)+σ(ai,bj), D(i, j), I(i, j)} • S(i, j) = S(0, j) – q for j 0 D(i – 1, 0) – r for i 0 max{S(i–1, j) – q - r, D(i – 1, j) – r} S(i, 0) – q for i 0 I(0, j – 1) – r for j 0 max{S(i, j–1) – q - r, I(i – 1, j) – r} • D(i, j) = • I(i, j) = 动态规划法的递归式 算法设计与分析
动态规划法求序列相似性 • Alignment(int m, int n, char A[m], char B[n]) • int S[m][n], D[m][n], I[m][n]; • { • 初始化; • 从(i, j) = (1, 1)到(m, n)逐个计算并填写 D[i, j] 、I[i, j]和S[i, j]; • 输出S(m, n); • } 算法设计与分析
由递归式 可知S(i, j)依赖于S(i–1, j–1)、 D(i, j)和I(i, j)。 0 m = 0, n = 0 D(i, 0) for j 0 I(0, j) for i 0 max{S(i–1, j–1)+σ(ai,bj), D(i, j), I(i, j)} • 由递归式 可知D(i, j)依赖于S(i – 1, j)、 D(i – 1, j) 。 • 由递归式 可知D(i, j)依赖于S(i – 1, j)、 D(i – 1, j) 。 S(i, 0) – q for i 0 I(0, j – 1) – r for j 0 max{S(i, j – 1) – q - r, I(i – 1, j) – r} S(0, j) – q for j 0 D(i – 1, 0) – r for i 0 max{S(i–1, j) – q - r, D(i – 1, j) – r} S(i, j) = I(i, j) = D(i, j) = I(i–1, j) S(i–1, j–1) S(i, j–1) S(i–1, j) D(i, j) I(i, j) D(i–1, j) I(i, j) S(i, j) D(i, j) 递归式中的数据依赖 • 递归式中的数据依赖关系如下面的三个图所示。在用循环程序实现这些递归式时,必须要保证在计算的过程中能够满足这里的数据依赖关系。 算法设计与分析
j j j 0 0 0 i i i I[m, n] D[m, n] S[m, n] I(i–1, j) S(i–1, j–1) S(i, j–1) S(i–1, j) D(i, j) I(i, j) D(i–1, j) I(i, j) S(i, j) D(i, j) 递归式中的数据依赖 • 由数据依赖关系可知,:在i和j方向上按D[i, j]、I[i, j]、S[i, j]逐个进行是可行的计算。 D[i, j] I[i, j] S[i, j] 算法设计与分析
初始化的工作 • Initialization ( ){ • S[0][0] = 0; D[0][0] = –q; I[0][0] = –q • for (int i = 1; i <= m; i++) • {D[i][0] = D[i –1][0] – r; S[i][0] = D[i][0]; • I[i][0] = S[i][0] – q;} • for (int j = 1; j <= n; j++) • {I[0][j] = I[0][j –1] – r; S[0][j] = I[0][j]; • D[0][j] = S[0][j] – q;} • } 算法设计与分析
S[i, j]、D[i, j]和I[i, j]的计算 • while (i <=m && j <= n) do { • for (int t = j; t <= n; t++) • {D[i][t] = max{D[i–1][t] – r, S[i–1][t] – q – r}; • I[i][t] = max{I[i][t–1] – r, S[i][t–1] – q – r}; • S[i][t] = max{S[i –1][t–1] +σ(ai,bt), D[i][t], I[i][t]};} • for (int t = i; t <= m; t++) • D[i][t] = max{D[i–1][t] – r, S[i–1][t] – q – r}; • I[i][t] = max{I[i][t–1] – r, S[i][t–1] – q – r}; • S[i][t] = max{S[i –1][t–1] +σ(ai,bt), D[i][t], I[i][t]};} • i++; j ++} 算法设计与分析
动态规划总结 • 与分治法相比,相同之处是也将原问题分解为若干子问题,再递归求解;不同之处是所分解的子问题彼此并不独立,而是互有重叠。 • 基本思想是造表记录已解的子问题,再次遇到时仅查表即可,避免了重复计算,提高效率。 • 通常用于求解具有最优性质的问题,而且其子问题也具有最优性质(最优子结构性质)。 • 实现方法通常为自底向上的递归形式,但也可以采用自上而下的形式(备忘录方法)。 算法设计与分析