矩阵链乘法和动态规划原理

来源:互联网 发布:apache ant 1.7.1安装 编辑:程序博客网 时间:2024/05/29 10:37

</pre>矩阵链乘法<p></p><p><span style="font-size:18px">给定一个n个矩阵的序列(矩阵链),希望求其成积。<A1,A2,A3.....An>;</span></p><p><span style="font-size:18px">可以先用括号明确计算顺序,然后利用标准矩阵求积的方法计算,不同的加括号方式运算结果相同,但是运算的时间可能差别非常大,所以这里想要探究的就是怎样加括号可以使得运算最快</span></p><p><span style="font-size:18px"></span></p><p><span style="font-size:18px">首先,我们称具有这样性质的矩阵链是<strong>完全括号化</strong>的:</span></p><p><span style="font-size:18px">它是单一矩阵,或者是两个完全括号化的矩阵乘积链的积,且已外加括号</span></p><p><span style="font-size:18px">对矩阵链加括号的方式会对乘积运算的代价产生巨大影响,当A矩阵为 p*q型, B矩阵为q*r型,那么乘积C 为p*r型,代价为 p*q*r</span></p><p><span style="font-size:18px">以矩阵链<A1,A2,A3>为例来说明不同的加括号方法对运算代价的影响,假设三个矩阵分别为 10*100, 100*5,5*50,</span></p><p><span style="font-size:18px">((A1A2)A3)代价为 10*100*5+10*5*50 = 7500次, (A1(A2A3))先算括号里 100*5*50 = 25000,得到100*50的矩阵与前面的A1乘 10*100*50=50000次 共75000,因此第一种比第二种计算要快十倍</span></p><p><span style="font-size:18px"></span></p><p><span style="font-size:18px">矩阵链乘法问题可以描述如下:给定n个矩阵的链<A1,A2,A3..An>,矩阵Ai的规模为pi-1 * pi,求完全括号化方案使得计算乘积所需的标量乘法次数最少</span></p><p><span style="font-size:18px">普通的穷举法肯定是效率低下的,现在用动态规划来处理,根据动态规划的四个步骤进行:</span></p><p><span style="font-size:18px">1、刻画一个最优解的结构特征</span></p><p><span style="font-size:18px">2、递归的定义最优解的值</span></p><p><span style="font-size:18px">3、计算最优解的值,通常采用自底向上的方法</span></p><p><span style="font-size:18px">4、利用计算出的信息构造最优解</span></p><p><span style="font-size:18px"><strong>S1:最优括号化方案的结构特征</strong></span></p><p><span style="font-size:18px">用Ai..j 表示  Ai....Aj的乘积,当i<j时为了对矩阵链进行括号化,就必须在某个 Ak和Ak+1之间加括号,因此对矩阵链,先计算 Ai...Ak,再计算 Ak+1...Aj,再将两者相乘, 而对应的划分好的这两部分进行括号化时,就是直接采用独立对其求解时所得的最优方案(将前半部分或者后半部分看做独立的实例进行最优化处理时得到的加括号方案)。</span></p><p><span style="font-size:18px">可以看到,一个非平凡的矩阵链乘法的问题实例的任何解都要划分链,而任何最优解都是由子问题实例的最优解构成的</span></p><p><span style="font-size:18px"><strong>S2:一个递归的求解方案</strong></span></p><p><span style="font-size:18px">下面用子问题的最优解俩递归的定义原问题最优解的代价,将对所有1<=i<=j<=n确定<span style="font-size:18px"> Ai....Aj的最小代价括号化方案作为子问题,令 m[i,j]表示计算Ai..j时所需标量乘法的最小值,那么原问题的最优解就是m[1,n];</span></span></p><p><span style="font-size:18px">首先,当i=j时,对所有i=1...n, m[i,i]  = 0; i<j时,用最优子结构来求,假定分割点在Ak Ak+1,那么就要计算Ai..k,Ak+1..j加上两者相乘的代价  其中Ai = pi-1*pi</span></p><p><span style="font-size:18px">m[i,j] = m[i,k] + m[k+1,j] + pi-1*pk*pj(i<j)</span></p><p><span style="font-size:18px">此公式假定k是已知的,但实际不知道,不过K只有 j-i种可能的取值即k= i, i+1....j-1,只要检查这些情况找到最优的即可。m[i,j]给出了最小代价,但是没有记录划分方案,所以用s[i,j]保存最优括号化方案的分割点位置k</span></p><p><span style="font-size:18px"><strong>S3:计算最优代价</strong></span></p><p><span style="font-size:18px">基于上面的m[i,j]计算公式可以设计出递归的算法了,但是从前民斐波那契、钢条切割的问题知道递归的算法代价很大,因为具有动态规划最优子结构和子问题重叠的特点,所以可以改成动态规划的算法。这里用自底向上的表格法,Ai的规模为pi-1*pi (i=1,2,3..n), p = <p0,p1,p2..pn>, p.length = n+1;    利用m[1..n, 1..n]来保存代价m[i,j],  </span></p><p><span style="font-size:18px">用s[1..n-1 , 2..n]保存最优值m[i,j]的分割点k </span></p><p><span style="font-size:18px">伪代码如下:</span></p><p><span style="font-size:18px"></span></p><pre name="code" class="cpp">
Martix_Chain_Order(p)

{

n = p.length-1;//矩阵链的长度

let m[1..n , 1..n], s[1..n-1 , 2..n]be new tables;

for(int i = 1;i<=n;i++)

m[i,i] = 0;//只有一个矩阵的情况,计算0次

for(int l = 2; l <=n; l++)//l表示当前计算的矩阵链的长度

{

for(int i = 1;i<=n-l+1;i++)

{

int j = i+l-1;

m[i , j] = 10000000;//初始化为一个很大的数

for(int k = i;k<=j-1;k++)//遍历所有可能的分割点

{

q = m[i,k]+m[k+1,j]+pi-1*pk*pj;

if(q<=m[i,j])//这里用等号是为了有两种都是最优时,选择括号加在前面的

{m[i,j] = q; s[i,j] = k;}

}

}

}

}

S4:构造最优解

Print_Results(s[,], i, j)

{

if(i==j)

print "A"i;

else {print"(";

Print_Results(s, i, s[i,j]);

Print_Results(s,s[i,j]+1,j);

print")";

}

}
调用Print_Results(s, 1, n);



动态规划原理

在什么情况下该寻求动态规划解决问题呢?适合用动态规划解决的最优化问题应该具备两个要素:最优子结构和子问题重叠  

用动态规划求解最优化问题的第一步就是刻画最优解的结构,如果一个问题的最优解包含其子问题的最优解,就称此问题具有最优子结构性质。在发掘最优子结构性质的过程中,实际遵循如下的通用模式:

1、证明问题最优解的第一个组成部分是做出一个选择,例如选择钢条第一次切割的位置,选择划分矩阵链的位置。做出这个选择会产生一个或多个子问题。

2、对于一个问题在其可能的第一步选择中,假定已经知道哪种选择能获得最优解

3、给定可获得最优解的选择后,确定这次选择会产生哪些子问题,以及如何最好的刻画子问题的空间

4、证明:作为构成原问题的最优解的组成部分,每个子问题的解就是它本身的最优解

在尝试使用动态规划时要小心,要注意是否具有最优子结构的性质


适合用动态规划方法求解的最优化问题应该具备的第二个性质是子问题空间必须足够“小”,即问题的递归算法会反复的求解相同的子问题,而不是一直生成新的子问题


0 0
原创粉丝点击