1.问题描述
给定n个矩阵构成的一个链<A1,A2,A3,…An>,其中i=1,2,…n,矩阵A的维数为pi-1pi,对乘积 A1A2…An 以一种最小化标量乘法次数的方式进行加全部括号。
注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。
2.问题分析
- 最优加全部括号的结构
动态规划第一步是寻找一个最优的子结构。假设现在要计算AiAi+1…Aj的值,计算Ai…j过程当中肯定会存在某个k值(i<=k<j)将Ai…j分成两部分,使得Ai…j的计算量最小。分成两个子问题Ai…k和Ak+1…j,需要继续递归寻找这两个子问题的最优解。
有分析可以到最优子结构为:假设AiAi+1…Aj的一个最优加全括号把乘积在Ak和Ak+1之间分开,则Ai…k和Ak+1…j也都是最优加全括号的。- 一个递归解
设m[i,j]为计算机矩阵Ai…j所需的标量乘法运算次数的最小值,对此计算A1…n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:
当i==j时:m[i,j] = 0,(此时只包含一个矩阵)
当i<j 时:从步骤1中需要寻找一个k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)。- 计算最优代价
虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2…n。输入序列为:p=<p0,p1,…pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下:- 构造一个最优解
第三步中已经计算出来最小代价,并保存了相关的记录信息。因此只需对s表格进行递归调用展开既可以得到一个最优解。
3.编程分析
算法程序分为两部分,计算部分和递归输出部分
- 计算部分
- 分析
假设需要计算 i 个矩阵 相乘,则任意两个矩阵之间都需要计算一次代价(自己到自己也需要计算,虽然一定为0),共需要1+2+3+…+i 次,存储到m矩阵(代价矩阵)是一个1到 i的阶梯型,举例当i=5时,m矩阵为
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | * | ||||
2 | * | * | |||
3 | * | * | * | ||
4 | * | * | * | * | |
5 | * | * | * | * | * |
再来看s矩阵,s矩阵存储的是 i 矩阵乘到 j 矩阵时最优的分割处 k的取值,与m矩阵不同的是,任意两个矩阵之间都需要计算 k值但是自己到自己不需要计算,共需要1+2+3+…+(i-1)次,存储到s矩阵(k值矩阵)是一个1到 i-1的阶梯型,举例当i=5时,s矩阵为
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | |||||
2 | * | ||||
3 | * | * | |||
4 | * | * | * | ||
5 | * | * | * | * |
可以发现s矩阵和m矩阵刚好可以放在同一个 i*i 的矩阵中,这样我们编程的时候就可以只定义一个 i*i 数组,将s矩阵存放在m矩阵对角的位置,循环变量我们准备使用一个矩阵位置 j 和要计算的另一个矩阵到 j 矩阵的距离 n ,减少资源的占用
- 代码
预编译部分,定义全局变量
#define Matrix_MUM 8
int min_s,min_k,M[Matrix_MUM][Matrix_MUM]={
0},p[Matrix_MUM+1]={
4,5,3,6,4,5,3,6,4};
main()
函数,n 是 i 到 j 相差的矩阵数,用到了 j 和 n 两个变量,i 可以通过 j 和 n 计算出来
int main()
{
int j,n;
for(n=1;n<Matrix_MUM;n++)
for(j=n;j<Matrix_MUM;j++)
chain(j,j-n),M[j-n][j]=min_s,M[j][j-n]=min_k;
}
chain()
函数,计算k值
void chain(int j,int i)//计算
{
int s,k;
for(k