矩阵链乘法要用到动态规划的思想,和分治相类似,都是通过组合子问题的解来求解原问题,但是与分治不同的是,分治要求子问题与原问题只有规模大小的区别,而动态规划要求子问题拥有重叠的子子问题。这样通过动态规划自底向上的求解,就能求出每个子问题的最优解,避免了重复计算。
例:
求该矩阵的最小运算量,我们不关心矩阵中的元素到底是什么,我们只关心要进行多少次运算。
例如A1和A2相乘需要30*35*15次运算。
定义从第i个矩阵到第j个矩阵的运算量为m[i,j]
则可以得到
当i=j时,自然是0,因为只有一个矩阵不需要进行运算。
当i≤j时,我们让k从i+1到j-1遍历,比较找到最小的那个以确定分隔的位置。
在计算好之后,我们将这个数值记录,这样在计算下一个规模更大的子问题时,就不用再重复计算了。
例如我们通过计算得到A1,A2,A3需要先算A2*A3,再乘A1,那么在计算A1,A2,A3,A4的时候,就可以直接拿来用。
建立一个m[i,j]用于记录运算量,建立一个s[i,j]用于记录分隔点
举例说明一下这两个表是怎么算的,例如m[1,3]和s[1,3],首先我们可以看到A1*A2*A3只有两种情况,即(A1(A2A3))和((A1A2)A3),第一种情况为2625+30*35*5=7875,第二种情况为15750+30*15*5=18000,显然第一种情况更小,所以应在A1,A2之间分隔,即m[1,3]=7875,s[1,3]=1
剩下的情况类似,直到最后算得m[1,6]即为该矩阵链乘法的最小运算量。
代码如下:
public class Test0202 {
private static int[][] m;//用于记录运算量
private static int[][] s;//用于记录分隔点
private static int[] p;//用于记录矩阵链
public static void main(String[] args) {
init ();
matrixChain ();
}
public static void init () {
m = new int[6][6];
s = new int[6][6];
p = new int[7];
for (int i = 0; i < p.length; i++) {
p[i] = random ();
}
for (int i = 0; i < p.length; i++) {
System.out.print (p[i] + " ");
}
System.out.println ();
}
public static int random () {
return 1 + (int)(Math.random () * 100);
}
public static void matrixChain () {
int n = m.length;
//对角线上的值为0,矩阵不会和自己相乘
for (int i = 0; i < n; i++) {
m[i][i] = 0;
}
//矩阵规模,从2个到n个
for (int L = 2; L <= n; L++) {
//表示一共有多少种取法,比如6个矩阵取2个就有5种取法
//i记为已取矩阵的头矩阵
for (int i = 0; i < n - L + 1; i++) {
//j记为已取矩阵的尾矩阵
int j = i + L - 1;
//初始化m[i][j]的值为无限大
m[i][j] = Integer.MAX_VALUE;
for (int k = i; k <= j - 1; k++) {
int q = m[i][k] + m[k + 1][j] + p[i]*p[k + 1]*p[j + 1];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
}
}
System.out.println ("该矩阵链的最小计算量是:" + m[0][n - 1]);
System.out.println ("计算量记录表");
for(int i = 0; i < m.length; i++){
for(int j = 0; j < m[i].length; j++){
System.out.printf("%8d", m[i][j]);
}
System.out.println();
}
System.out.println ("分隔点记录表");
for(int i = 0; i < s.length; i++){
for(int j = 0; j < s[i].length; j++){
if (i >= j) {
System.out.printf ("%8d", 0);
} else {
System.out.printf ("%8d", s[i][j] + 1);
}
}
System.out.println();
}
print (s, 0, n - 1);
}
public static void print (int[][] s, int i, int j) {
if (i == j) {
System.out.print ("A" + (i + 1));
} else {
System.out.print ("(");
print (s, i, s[i][j]);
print (s, s[i][j] + 1, j);
System.out.print (")");
}
}
}