矩阵链乘法
问题描述:给定n个矩阵构成的一个链<A[1], A[2], ... , A[n]>,其中i = 1, 2, ... , n,矩阵A[i]的维数为p[i-1]*p[i],对乘积A[1]A[2]...A[n]以一种最小化标量乘法次数的方式进行加全部括号。
步骤1:最优加全部括号的结构
用记号A[i..j]表示乘积A[i]A[i+1]...A[j]求值的结果,其中i <= j。假设A[i]A[i+1]...A[j]的一个最优加全部括号把乘积在A[k]和A[k+1]之间分开,则对A[i]A[i+1]...A[j]最优加全部括号的“前缀”子链A[i]A[i+1]...A[k]的加全部括号必须是A[i]A[i+1]...A[k]的一个最优加全部括号。
步骤2:一个递归解
设m[i][j]为计算矩阵A[i..j]所需的标量乘法运算次数的最小值;对整个问题,计算A[1..n]的最小代价就是m[1][n]。
假设最优加全部括号将乘积A[i]A[i+1]...A[j]从A[k]和A[k+1]之间分开,i <= k < j。则:
m[i][j] = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]
关于对乘积A[i]A[i+1]...A[j]加全部括号的最小代价的递归定义为:
m[i][j] = 0 if i == j
m[i][j] = min(i<=k<j){m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]} if i < j
步骤3:计算最优代价
用迭代自底向上的表格法来计算最优代价。
步骤4:构造一个最优解
利用保存在表格s[n][n]内的、经过计算的信息来构造一个最优解。按最优方式计算A[1..n]时,最终矩阵相乘次序是A[1..s[1][n]]A[a[1][n]+1..n]。之前的乘法可以递归地进行。
MatrixChainMain.c
#include < limits.h >
#define LENGTH 6
/**/ /*
*p[i]:矩阵Ai的列数或Ai-1的行数
*m[i][j]:纪录Ai - Aj的矩阵连乘的最小代价
*s[i][j]:纪录Ai - Aj之间得到最小连乘代价时的分割点
*/
void MatrixChainOrder( int p[], int m[][LENGTH], int s[][LENGTH])
... {
int i, j, k, len, q;
for(i=0; i<LENGTH; i++)
m[i][i] = 0;
for(len=2; len<=LENGTH; len++) //len为矩阵链的长度
...{
for(i=0; i<(LENGTH-len+1); i++)
...{
j = i + len - 1;
m[i][j] = INT_MAX;
//计算Ai - Aj最优代价
for(k=i; k<j; k++)
...{
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;
}
}
}
}
}
void PrintOptimalParens( int s[][LENGTH], int i, int j)
... {
if(i == j)
printf("A%d", i);
else
...{
printf("(");
PrintOptimalParens(s, i, s[i][j]);
PrintOptimalParens(s, s[i][j]+1, j);
printf(")");
}
}
int main( int argc, char ** argv)
... {
int p[LENGTH+1] = ...{30, 35, 15, 5, 10, 20, 25};
int m[LENGTH][LENGTH];
int s[LENGTH][LENGTH];
MatrixChainOrder(p, m, s);
PrintOptimalParens(s, 0, LENGTH-1);
printf(" ");
return 0;
}