动态规划_矩阵链连乘问题

来源:互联网 发布:软件项目验收阶段 编辑:程序博客网 时间:2024/06/05 10:54

       矩阵链连乘问题:由于不同的矩阵相乘顺序会导致不同的矩阵标量元素相乘的操作数,求设矩阵队列A1A2A3...An-1An的最小相乘操作数。

      好吧,先让我们回顾一下线性代数中矩阵乘法的相关知识(其实看到这个题目时我已经忘了两个矩阵是怎么相乘的了,刚好回顾一下),两个矩阵只有相容才能相乘。即A x B要求A的列数等于B的行数。如[n * m]和[m * p]的两个矩阵是可以相乘的,且它们相乘的标量运算数为n * m * p。那么不同的相乘顺序又为什么会有不同的操作数呢?可以这样举例证明:现有三个矩阵A1,A2,A3对应的规模分别是{5 x 100},{100 x 20},{20 x 50}。

       a.若采用(A1(A2A3))的相乘顺序,先进行A2A3相乘,相乘次数为100 x 20 x 50 = 100000,得到一个{100 x 50}的矩阵;再将结果和A1相乘,又需要 5 x100 x 50 = 25000;总共需要乘操作数为:25000+100000 = 125000

       b.若采用((A1A2)A3)的相乘顺序,先进行A1A2相乘,相乘次数为5 x 100 x 20 = 10000,得到一个{5 x 20}的矩阵,再将结果和A3相乘,有需要5 x 20 x 50 = 5000;总共需要乘操作数为:10000+5000 = 15000

      由此可见不同的相乘顺序需要不同的相乘次数。也可以这么理解,矩阵{m x n}和矩阵{n x p} 会得到一个{m x p}的矩阵,由此可见相乘后的矩阵规模有p决定。

状态转移方程如下(其中m[i][j]表示从Ai到Aj的最优解)

代码如下:

package com.wly.algorithmbase.dailyproblem;/** * 动态规划解最小矩阵连乘操作数问题 * @author wly * */public class MinMatrixChainOperation {static int MAX_INFINITE = 999999999;public static void main(String[] args) {//构造测试数据,如有数组{3,5,20,100}对应矩阵A1{3x5},A2{5x20},A3{20x100}//int[] testMatrice = {5,10,50,20,10,40,30};int[] testMatrice = {5,3,4,2};System.out.println(slove(testMatrice));}/** * 求解问题,由于使用数组做结果容器,所以当要表示子链AiAi+1...Aj在数组中的实际表现为result[i-1][j-1] * 下面的代码的变量都尽量用其对应的功能命名,但是代码会显得很绕,不过这样便于理解。在这之后又写了一个简化了变量的方法,以便于阅读 * 每个矩阵链A1A2A3...An-1An都可以分割成A1A2A3...Ak和Ak+1Ak+2...An两个子举证连,即 * F(A1A2A3...An-1An) = F(A1A2A3...Ak) + F(Ak+1Ak+2..An) + A1AkAn * @param 矩阵链的行,列数数组 * @return 最小乘操作数 */public static int slove(int[] p) {int matrixNum = p.length - 1; //矩阵链中包含的矩阵个数//结果集,result[i][j]表示矩阵链中从Ai到Aj的最优解,注意矩阵链是从A1开始,而不是A0开始的int[][] result = new int[matrixNum][matrixNum]; //最优解,divide[i][j]表示子矩阵链Ai到Aj的最优解的分割点int[][] separate = new int[matrixNum][matrixNum]; //当i==j时,解为0for(int i=0;i<result[0].length;i++) {result[i][i] = 0;}//求解各个不同规模的子矩阵链的解for(int subLen=2;subLen<=matrixNum;subLen++) {//移动子矩阵链的起点位置for(int startIndex=1;startIndex<=(matrixNum-subLen+1);startIndex++) {//将未计算的可能解置为"最差解",注意这里索引的纠偏"-1"result[startIndex-1][startIndex+subLen-1-1] = MAX_INFINITE;//根据状态转移方程,求解从startIndex到(startIndex+subLen)的子链的最优解for(int divideP=startIndex;divideP<(startIndex+subLen-1);divideP++) {if(result[startIndex-1][startIndex+subLen-1-1] > (result[startIndex-1][divideP-1]+ result[divideP-1+1][startIndex+subLen-1-1] + p[startIndex-1]*p[divideP]*p[startIndex+subLen-1])) {result[startIndex-1][startIndex+subLen-1-1] = result[startIndex-1][divideP-1]+ result[divideP-1+1][startIndex+subLen-1-1] + p[startIndex-1]*p[divideP]*p[startIndex+subLen-1];separate[startIndex-1][startIndex+subLen-1-1] = divideP;}}}}printSeparate(separate,0,separate[0].length-1);return result[1-1][matrixNum-1];}/** * 上面方法的简化版,主要简化了变量名 * 之所以这样是因为数组索引是基于0的,而状态转移中的r[i][j]表示从i到j的子链的最优解,实际在数组中进行-1纠偏动作 * @param p * @return */public static int sloveSimplify(int[] p) {int n = p.length - 1; //矩阵链中包含的矩阵个数//结果集,result[i][j]表示矩阵链中从Ai到Aj的最优解,注意矩阵链是从A1开始,而不是A0开始的int[][] r = new int[n][n]; //最优解,divide[i][j]表示子矩阵链Ai到Aj的最优解的分割点int[][] s = new int[n][n]; //当i==j时,解为0for(int i=0;i<r[0].length;i++) {r[i][i] = 0;}//求解各个不同规模的子矩阵链的解for(int len=2;len<=n;len++) {for(int i=1;i<=(n-len+1);i++) { //移动子矩阵链的起点位置int j = i+len-1; //子链终止点r[i-1][j-1] = MAX_INFINITE;//将未计算的可能解置为"最差解",注意这里索引的纠偏"-1"//根据状态转移方程,求解从startIndex到(startIndex+subLen)的子链的最优解for(int divideP=i;divideP<j;divideP++) {if(r[i-1][j-1] > (r[i-1][divideP-1]+ r[divideP-1+1][j-1] + p[i-1]*p[divideP]*p[j])) {r[i-1][j-1] = r[i-1][divideP-1]+ r[divideP-1+1][j-1] + p[i-1]*p[divideP]*p[j];s[i-1][j-1] = divideP;}}}}printSeparate(s,0,s[0].length-1);return r[1-1][n-1];}/** * 打印最优解分割方式 * @param separate * @param start * @param end */private static void printSeparate(int[][] separate,int start,int end) {if(end <= (start+1)) {return;} else {int d = separate[start][end];System.out.println(d);printSeparate(separate,start,d-1);printSeparate(separate,d,end);}}}

       运行结果:

154
       其中第一行的表示只有分割点的位置在A1和A2之间,即为((A1)(A2A3)),可以口算验证一下:

       (A1(A2A3))中,先计算A2A3 = 3*4*2 = 24.得到3*2的矩阵,再和A1乘,又要5*3*2=30,总共30+24=54,另外一种可能((A1A2)A3)中,先计算A1AA2=5*3*4=60,得到5*4的矩阵,再和A3,又要5*4*2=40,总共60+40=100。所以验证通过

       最后总结一下本人对动态规划思想的理解:

       1、动态规划并不能使需要遍历的可能解变得更少(这个我刚开始看的时候还以为可以呢),只是在求解过程中,人为的安排子问题的求解顺序,使规模较小的子问题首先被求解,并将其解保存起来,那么在求解更高层次的子问题时,就可以直接利用保存起来的子问题的解了,而不需要再次重新求解其子问题的解了。这是一种以空间代价换取时间的做法,当然这些话,每本算法书上都有,不过要做到真的体会理解,还是需要自己动手写代码的。

       2、关于最优子问题结构的论证,可以采用反证法,如:矩阵链A1A2A3A4...An的最优解的是A1A2A3...Ak和Ak+1Ak+2...An,则如果存在另外一种更有的分割点m使得A1A2A3...Am和Am+1Am+2An这种分割方式的解更优,则k分割点就不是当前问题的最优解了,矛盾。

       3、关于重叠子问题特性,指的是同一个子问题,只是作为不同的问题的子问题出现而已。与分治法不同的是,动态规范中不同规模的子问题可能共有了某个子子问题的解。

       至此动态规划的学习暂时告以段落,以后会做些习题巩固理解。

       O啦~~~

       转载请保留出处:http://blog.csdn.net/u011638883/article/details/16338797

       谢谢!!

 


原创粉丝点击