今天下午笔试遇到了矩阵链乘法,谨在此总结一下。
分析
关于本题的分析,动态规划之矩阵链乘法理解 已经讲的很详细了,我在此仅简单总结一下:
A
1
∗
A
2
A_1*A_2
A1∗A2生成一个维度是
[
p
0
,
p
2
]
[p_0, p_2]
[p0,p2]的矩阵 (
A
1
A_1
A1的维度为
[
p
0
,
p
1
]
[p_0, p_1]
[p0,p1],
A
2
A_2
A2的维度为
[
p
1
,
p
2
]
[p_1, p_2]
[p1,p2]),共有
p
0
∗
p
2
p_0*p_2
p0∗p2个元素,每个元素由
p
1
p_1
p1次乘法得到,因此一共算法复杂度
p
0
∗
p
2
∗
p
1
p_0*p_2*p_1
p0∗p2∗p1。
同理,对于
A
i
k
,
A
k
,
A
k
j
A_ik, A_k,A_kj
Aik,Ak,Akj这三个矩阵连乘,复杂度为
p
i
∗
p
k
∗
p
j
p_i*p_k*p_j
pi∗pk∗pj,因此对于从第
i
i
i个到第
j
j
j个矩阵连乘,其最小代价的递归式可以写成:
于是问题的关键就归结为如何选择
k
k
k,事实上这会存在很多重复计算的子结构,所以需要循环来找最小的代价,当k=i……j时k每对应一个值就得继续往下递归,此时又得循环一遍k,在其中就会有许多子问题重复计算了。
刚才我们分析的是采用自上向下的方法,而实际上动态规划使用的是自底向上的方法,它在划分中把一些明显代价大的,可以比较的pk掉,从而减少分析次数,避免子问题的重复计算。
代码
# s记录最优值m[i,j]对应的分割点k
# dp记录代价m[i,j]
def matrixChainOrder(p):
n = len(p)
m = [[0 for _ in range(n)] for _ in range(n)]
s = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
m[i][i] = 0
for l in range(2, n): # l 表示选定的链长
for i in range(1, n-l+1): # i 表示可以划定成多少种
j = i+l-1 # j 表示划定的子链的末尾
m[i][j] = float('inf')
for k in range(i, j): # 寻求最优k使得子结构最优
q = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]
if q < m[i][j]:
m[i][j] = q
s[i][j] = k
return m, s
#输出矩阵链的最优括号化方法
def printOpt(s, i, j):
if i == j:
print("A"+str(i), end=' ')
else:
print("(", end=' ')
printOpt(s, i, s[i][j])
printOpt(s, s[i][j] + 1, j)
print(")", end=' ')
if __name__ == '__main__':
print("start=========")
p = [30, 35, 15, 5, 10, 20, 25]
m, s = matrixChainOrder(p)
# print(m)
# print(s)
print("最优括号化方案为:")
printOpt(s, 1, 6)
print("")
print("其标量乘法次数为:", m[1][6])
print("Over=========")