给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。考察这n个矩阵的连乘积A1A2…An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序,这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,则可以依此次序反复调用2个矩阵相乘的标准算法(有改进的方法,这里不考虑)计算出矩阵连乘积。若A是一个p×q矩阵,B是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。
例如,如果我们有四个矩阵A,B,C和D,我们将有:
(ABC)D =(AB)(CD)= A(BCD)= ….
不同组合得到的运算次数是不同的,例如A为 10 × 30 , B为 30 × 5 , C 为 5 × 60 那么
1 (AB)C = (10×30×5) + (10×5×60) = 1500 + 3000 = 4500 次运算
2 A(BC) = (30×5×60) + (10×30×60) = 9000 + 18000 = 27000 次运算
很明显第一种运算更为高效。
问题:给定一个数组P[]表示矩阵的链,使得第i个矩阵Ai 的维数为 p[i-1] x p[i].。我们需要写一个函数MatrixChainOrder()返回这个矩阵连相乘最小的运算次数。
示例:
输入:P [] = {40,20,30,10,30}
输出:26000
有4个矩阵维数为 40X20,20X30,30×10和10X30。
运算次数最少的计算方式为:
(A(BC))D – > 20 * 30 * 10 +40 * 20 * 10 +40 * 10 * 30
输入:P[] = {10,20,30,40,30}
输出:30000
有4个矩阵维数为 10×20,20X30,30X40和40X30。
运算次数最少的计算方式为:
((AB)C)D – > 10 * 20 * 30 +10 * 30 * 40 +10 * 40 * 30
1)最优子结构:
一个简单的解决办法是把括号放在所有可能的地方,计算每个位置的成本,并返回最小值。对于一个长度为n的链,我们有n-1种方法放置第一组括号。
例如,如果给定的链是4个矩阵。让矩阵连为ABCD,则有3种方式放第一组括号:A(BCD),(AB)CD和(ABC)D。
所以,当我们把一组括号,我们把问题分解成更小的尺寸的子问题。因此,这个问题具有最优子结构性质,可以使用递归容易解决。
2)重叠子问题
具体实例及实现代码如下所示:
/** * @Title: MatrixChainOrder.java * @Package dynamicprogramming * @Description: TODO * @author peidong * @date 2017-6-7 上午9:38:05 * @version V1.0 */ package dynamicprogramming; /** * @ClassName: MatrixChainOrder * @Description: 矩阵连乘问题 * @date 2017-6-7 上午9:38:05 * */ public class MatrixChainOrder { /** * * @Title: matrixChain * @Description: 动态规划矩阵连乘次数 * @param p * @param m * @param s * @return void * @throws */ public static void matrixChain(int[]p, int[][]m, int [][]s) { int n=p.length-1; for (int i=1;i<=n;i++) m[i][i] = 0; for (int r=2;r<=n;r++) for(int i=1;i<=n-r+1;i++){ int j=i+r-1; m[i][j] = 999999999; s[i][j] = i; for (int k = i; k < j; k++) { int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]; if (t < m[i][j]) { m[i][j] = t; s[i][j] = k;} } } } /** * * @Title: TraceBack * @Description: 最优解 * @param s * @param i * @param j * @return void * @throws */ public static void TraceBack(int [][]s, int i, int j) { if(i!=j){ TraceBack(s,i,s[i][j]); TraceBack(s,s[i][j]+1,j); System.out.println("Multiply A["+i+":"+s[i][j]+"] and A["+(s[i][j]+1)+":"+j+"]"); } } /** * * @Title: OptimalParens * @Description: 打印求解过程 * @param s * @param i * @param j * @return void * @throws */ public static void OptimalParens(int[][] s,int i,int j){ if(i==j) System.out.print("(A"+i); else{ OptimalParens(s,i,s[i][j]); OptimalParens(s,s[i][j]+1,j); System.out.print(")"); } } /** * * @Title: main * @Description:测试用例 * @param agrs * @return void * @throws */ public static void main(String agrs[]) { int[] p={30,35,15,5,10,80,25,65,40,20}; int n=p.length; int [][]m=new int [n][n]; int [][]s=new int [n][n]; matrixChain(p, m, s); System.out.println("该矩阵阶乘子问题数乘的次数:"); for(int i=1;i<m.length;i++){ for(int j=1;j<m.length;j++){ if(i>j) { System.out.print("----"+"\t"); } else { System.out.print(m[i][j]+"\t"); } } System.out.println(); } System.out.println(); System.out.println("该矩阵阶乘子问题数乘的次数:"); for(int i=1;i<s.length;i++){ for(int j=1;j<s.length;j++){ if(i>j) { System.out.print("----"+"\t"); } else { System.out.print(s[i][j]+"\t"); } } System.out.println(); } System.out.println(); System.out.println("该矩阵阶乘的最优解:"); TraceBack(s,1,n-1); OptimalParens(s,1,n-1); } }