1,问题
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
2,枚举法(穷举搜索法)
枚举所有加括号的方式。
对于n个矩阵的连乘积,设其不同的计算次序为P(n)。每个问题可分解为两个子问题:(A1…Ak)(Ak+1…An)可以得到关于P(n)的递推式如下:
以上递推关系说明,P(n)是随n的增长呈指数增长的。因此,穷举法不是一个多项式时间复杂度算法。
3,备忘录算法
1)概念
备忘录方法是动态规划算法的变形。用表格保存子问题答案,避免重复计算。
与动态规划不同的是:备忘录方法的递归是自顶向下的,而动态规划是自底向上的。
备忘录方法为每个问题建立一个记录项(如赋初值为0),初始化时,该记录项存入一个特殊值,表示该问题尚未求解。
2)分析
上图为枚举过程,其中黑色方格为重复计算。备忘录就是用来保存计算结果,在每次计算前查表,如果计算过,则直接取值,避免重复计算。
4,动态递归算法
以矩阵链ABCD为例:
矩阵链长度为n。
n=1时,分别计算A、B、C、D最优值。
n=2时,分别计算AB、BC、CD最优值。
n=3时,分别计算ABC、BCD最优值。
n=4时,分别计算ABCD的最优值。
令:
k:为矩阵链断开的位置
d:数组存放矩阵链计算的最优值,d[i][j]是以第i个矩阵为首,第j个矩阵为尾的矩阵链的最优值,i > 0
m:存放矩阵链行列信息的数组,m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3…)
5,代码
package MatrixMultiply;
public class Main {
public static void main(String[] args) {
/**
* 6个矩阵连乘
* A1:30*35
* A2:35*15
* A3:15*5
* A4:5*10
*/
final int L = 7;
int[] p={30,35,15,5,10};
int N = MatrixMultiply.Best_Enum(p, 1, 4);
System.out.println("Best_Enum矩阵计算最少次数:" + N);
N = MatrixMultiply.Best_Memo(p, 1, 4);
System.out.println("Best_Memo矩阵计算最少次数:" + N);
N = MatrixMultiply.Best_DP(p, p.length);
System.out.println("Best_DP矩阵计算最少次数:" + N);
}
}
package MatrixMultiply;
public class MatrixMultiply {
private final static int MAX_INT = 99999999;// 表示无穷大
private final static int SIZE = 10;// 数组大小
/**
* 枚举法(递归算法)
*
* @param m
* 矩阵 m数组内存放矩阵链的行列信息 m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3...)
* @param left
* 左括号索引
* @param right
* 右括号索引
* @return
*/
public static int Best_Enum(int m[], int left, int right) {
// 只有一个矩阵时,返回计算次数0
if (left == right) {
return 0;
}
int min = MAX_INT; // 无穷大
int i;
// 括号依次加在第1、2、3...n-1个矩阵后面
for (i = left; i < right; i++) {
// 计算出这种完全加括号方式的计算次数
int count = Best_Enum(m, left, i) + Best_Enum(m, i + 1, right);
count += m[left - 1] * m[i] * m[right];
// 选出最小的
if (count < min) {
min = count;
}
}
return min;
}
public static int[][] memo = new int[SIZE][SIZE];
/**
* 备忘录法
*
* @param m
* m数组内存放矩阵链的行列信息 m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3...)
* @param left
* @param right
* @return
*/
public static int Best_Memo(int m[], int left, int right) {
// 只有一个矩阵时,返回计算次数0
if (left == right) {
return 0;
}
int min = MAX_INT;
int i;
// 括号依次加在第1、2、3...n-1个矩阵后面
for (i = left; i < right; i++) {
// 计算出这种完全加括号方式的计算次数
int count;
if (memo[left][i] == 0) {
memo[left][i] = Best_Memo(m, left, i);
}
count = memo[left][i];
if (memo[i + 1][right] == 0) {
memo[i + 1][right] = Best_Memo(m, i + 1, right);
}
count += memo[i + 1][right];
count += m[left - 1] * m[i] * m[right];
// 选出最小的
if (count < min) {
min = count;
}
}
return min;
}
private static int[][] d = new int[SIZE][SIZE]; // 存放矩阵链计算的最优值,d[i][j]为第i个矩阵到第j个矩阵的矩阵链的最优值,i
// > 0
/**
* 动态规划算法
*
* @param n
* @return
*/
public static int Best_DP(int[] m, int n) {
// 把d[i][i]置为0,1 <= i < n
for (int i = 0; i < SIZE; i++) {
java.util.Arrays.fill(d[i], 0);
}
int len;
// 递归计算矩阵链的连乘最优值
// len = 1,代表矩阵链由两个矩阵构成
for (len = 1; len < n; len++) {
int i, j, k;
for (i = 1, j = i + len; j < n; i++, j++) {
int min = MAX_INT; // 无穷大
for (k = i; k < j; k++) {
int count = d[i][k] + d[k + 1][j] + m[i - 1] * m[k] * m[j];
if (count < min) {
min = count;
}
}
d[i][j] = min;
}
}
return d[1][n - 1];
}
}