这类题目体现了DP的实质,也是经典问题。(•̀˓◞•́)
假设我们要用标准的矩阵乘法计算 M1 M 1 、 M2 M 2 、 M3 M 3 的乘积 M1M2M3 M 1 M 2 M 3 ,这三个矩阵的维数分别是2x10,10x2,2x10。
- 如果我们先把 M1 M 1 和 M2 M 2 相乘,然后把结果和 M3 M 3 相乘,即 ((M1M2)M3) ( ( M 1 M 2 ) M 3 ) 。那么要进行2x10x2+2x2x10=80次乘法;
- 如果我们先乘 M2 M 2 M3 M 3 ,结果再与 M1 M 1 相乘,即 (M1(M2M3)) ( M 1 ( M 2 M 3 ) ) 。那么数量乘法的次数就变成了:10x2x10+2x10x10=400。
可见,矩阵链相乘时的顺序不同,运算量也不同。而我们的目的是找到一种乘法顺序使得运算量最小。
递推关系式
我们注意到,对于矩阵链
M1M2...Mi
M
1
M
2
.
.
.
M
i
,矩阵
Mi
M
i
的列数一定等于矩阵
Mi+1
M
i
+
1
的行数(
1≤i<n
1
≤
i
<
n
),这是由矩阵乘法的定义决定的。
因此,对于一个矩阵链,我们指定每个矩阵的行数和最右面矩阵
Mn
M
n
的列数就可以了。假设有n+1维数
r1,r2,...,rn+1
r
1
,
r
2
,
.
.
.
,
r
n
+
1
,这里
ri
r
i
表示矩阵
Mi
M
i
的行数(
1≤i≤n
1
≤
i
≤
n
),
rn+1
r
n
+
1
表示最矩阵
Mn
M
n
的列数。
以后,我们用
Mi,j
M
i
,
j
来记
MiMi+1...Mj
M
i
M
i
+
1
.
.
.
M
j
的乘积。用
C[i][j]
C
[
i
]
[
j
]
来记录链
Mi,j
M
i
,
j
数量乘法的次数。
对于给定的一对索引
i
i
和,
Mi,j
M
i
,
j
可用如下方法计算:
设 k k 是和 j j 之间的一个索引,索引把矩阵链 Mi,j M i , j 分成了两部分: Mi,k−1=MiMi+1...Mk−1 M i , k − 1 = M i M i + 1 . . . M k − 1 和 Mk,j=MkMk+1...Mj M k , j = M k M k + 1 . . . M j 。所以 Mi,j=Mi,k−1Mk,j M i , j = M i , k − 1 M k , j 。
用这种方法计算 Mi,j M i , j 的耗费(即数量乘法的次数),是计算 Mi,k−1 M i , k − 1 的耗费加上计算 Mk,j M k , j 的耗费再加上 Mi,k−1 M i , k − 1 乘以 Mk,j M k , j 的耗费(它是 rirkrj+1 r i r k r j + 1 )。
我们需要遍历 k k ,找到使乘法所需的数量乘法最小的 k k 值,我们有以下递推式:
Mini<k≤j M i n i < k ≤ j {C[i][k−1]+C[k][j]+rirkrj+1} { C [ i ] [ k − 1 ] + C [ k ] [ j ] + r i r k r j + 1 }
为了找出 M1M2..Mn M 1 M 2 . . M n 的乘法次数,我们只需要解递推式:
C[1][n]= C [ 1 ] [ n ] = Min1<k≤n M i n 1 < k ≤ n {C[1][k−1]+C[k][n]+rirkrn+1} { C [ 1 ] [ k − 1 ] + C [ k ] [ n ] + r i r k r n + 1 }
填表
假设我们要求
n=6
n
=
6
个矩阵相乘。考虑下图:
对角线
d
d
用乘出各种个相继矩阵的最小耗费填满。特别地,对角线5恰好由一项组成,它表示6个矩阵相乘的最小耗费,这就是我们要求的结果。
我们从对角线0开始,到对角线5为止,沿着对角线填充这个三角形表。
- 首先在对角线0中,每个链仅由一个矩阵的组成,没有数量乘法,因此这个对角线填0。
- 接着,对角线1由两个连续的矩阵相乘的耗费来填充。如C[2][3]用 M2M3 M 2 M 3 的乘法耗费来填。
- 余下的对角线根据上面的递推式和先前存储在表中值来填。举例来说,C[2][5]的值为以下三个耗费的最小值:
– (1)计算 M2,2 M 2 , 2 的耗费(即C[2][2])加上计算 M3,5 M 3 , 5 (即C[3][5])的耗费,再加上 M2,2 M 2 , 2 乘以 M3,5 M 3 , 5 的耗费。
– (2)计算 M2,3 M 2 , 3 的耗费(即C[2][3])加上计算 M4,5 M 4 , 5 (即C[4][5])的耗费,再加上 M2,3 M 2 , 3 乘以 M4,5 M 4 , 5 的耗费。
– (3)计算 M2,4 M 2 , 4 的耗费(即C[2][4])加上计算 M5,5 M 5 , 5 (即C[5][5])的耗费,再加上 M2,4 M 2 , 4 乘以 M5,5 M 5 , 5 的耗费。
伪代码
下面我们给出算法的伪代码实现
MATCHAIN
输入
:n个矩阵的链的维数对应于正整数数组
r[1...n+1]
r
[
1...
n
+
1
]
,其中,
r[1...n]
r
[
1...
n
]
是n个矩阵的行数,
r[n+1]
r
[
n
+
1
]
是
Mn
M
n
的列数。
输出
:n个矩阵相乘的数量乘法的最小次数。
for i=1 to n {填充对角线d0}
C[i,i]=0;
end for
for d=1 to n-1 {填充对角线d1到dn-1}
for i=1 to n-d {填充对角线di的项目}
j=i+d
comment:下列三行计算C[i,j]
C[i,j]=inf
for k=i+1 to j
C[i,j]=Min{C[i,j],C[i,k-1]+C[k,j]+r[i]*r[k]*[j+1]
end for
end for
end for
return C[1,n]
时空复杂度
对于某个常数c>0,算法的运行时间正比于:
因此算法的时间复杂度是 Θ(n3) Θ ( n 3 )
显然,算法所需要的内存空间取决于所需要的三角数组的大小,也就是 Θ(n2) Θ ( n 2 ) 。