题目:
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少?
下面给出矩阵连乘的模板:
void MetrixChain(int p[],int n){
for(int i = 1;i <= n;i++){
m[i][i] = 0; //矩阵链长度为0
}
for(int r = 2;r <= n;r++){ //r控制矩阵链长度
for(int i = 1;i <= n-r+1;i++){ //i控制起点 j控制终点
int j = i+r-1;
// k = i的情形,为m[i][j]设置初始值,以便和res比较
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
s[i][j] = i;
for(int k = i+1;k < j;k++){ // i < k < j 的情形
int res = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
if(res < m[i][j]){
m[i][j] = res;
s[i][j] = k;
}
}
}
}
}
先给大家举一个例子,方便理解。
假设题目给出了6个矩阵以及每个矩阵的行数和列数,如下图所示:
我们可以根据这6个矩阵的行数和列数的规律,设定一个数组 p 来存放。
接下来我们画一个二维表格,行表示起点,列表示终点。每个方格表示从A[i]到A[j]的最少数乘次数 m[i][j]。例如 m[1][3]表示(A1A2A3)的最小数乘次数。注意:只有 i < j 的时候才有最小数乘次数。
我们按照矩阵链长度递增的方式依次计算 m[i][j]。
当矩阵链长度为1时,根据矩阵的性质可知,单个矩阵无法相乘,所以 (A1) = 0 ,(A2) = 0,以此类推。在表格中补全单个矩阵的最小数乘次数,如下图所示。
当矩阵链长度为2时,我们依次计算m[1][2],m[2][3],m[3][4],......,m[i][i+1](i <= n-2+1,理由见代码)。在代码里面矩阵链长度为2的不考虑 k 划分。因为 k 要满足 i <= k < j 条件,而此时 j = i + 1,所以 k 只能等于 i 。此时将 k = i 代入通用公式m[i][j] = m[i][k] + m[k+1][j] + p[i-1]×p[i]×p[j] 得到 m[i][j] = m[i][i] + m[i+1][j] + p[i-1]×p[i]×p[j] ,进一步化简可得m[i][j] = m[i+1][j] + p[i-1]×p[i]×p[j]。例如m[1][2] = ((A1)A2) = m[1][1] + m[2][2] + p[0]×p[1]×p[2] = 0 + 0 + p[0]×p[1]×p[2] = 30×35×15 = 15750。断开位置 k 为 i,故s[i][j] = i。
这里解释一下为什么代码里要先算出所有矩阵链长度为2的数乘次数。因为计算矩阵链长度为3的数乘次数其实就是在矩阵链长度为2的基础上,再和另一个矩阵相乘,以此类推。我们先把矩阵链长度小的全部算出来并存在二维数组里,这样在计算矩阵链长度大的数乘次数时就可以直接用,不用重复计算了。
当矩阵链长度为3时,我们依次计算m[1][3],m[2][4],......,m[i][i+2](i <= n-3+1,理由见代码)。例如m[2][4] = A2A3A4 ,这里的 k 就可以通过for循环来控制,用通用公式计算m[i][j] = m[i][k] + m[k+1][j] + p[i-1]×p[i]×p[j]。当 k = i+1时,m[2][4] = (A2A3)A4 = m[2][3] + m[4][4] + p[1]×p[3]×p[4] = 2625 + 0 + 35×5×10 = 4375 。算出来之后还需要和原来的值进行比较大小,看是否需要更新 m[i][j],s[i][j]。
当矩阵链长度为4时,最少数乘次数如下。
当矩阵链长度为5时,最少数乘次数如下。
当矩阵链长度为6时,最少数乘次数如下。
大家可以观察一下计算最少数乘次数 m[i][j] 的顺序,很有特色。按照矩阵链长度递增的方式依次计算 m[i][j]。
在此过程中,我们同时可以将每一次断开位置 k 记为 s[i][j]。
希望大家学有所获!