动态规划是算法分析与设计中一种重要的算法。其核心思想是将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法。
一、矩阵链相乘问题
例: 令P=<50,35,25,10,60,70,3,5,10>,相对应的矩阵链是:A1(50*35), A2(35*25), A3(25*10), A4(10*60), A5(60*70), A6(70*3), A7(3*5), A8(5*10), 括号内为矩阵的维度,请⽤动态规划算法确定⼀种乘法⽅式,使得A1*A2*A3*A4*A5*A6*A7*A8总的基本运算量(只算乘法,不算加法)最少。
二、求解
1.构建备忘录
备忘录是动态规划算法常用的工具,用来存储子问题得到的最优值并递归的更新其值,直到寻找到全局最优值为止。MatrixChain函数中定义了两个相同大小(n*n)的矩阵M和S,n为矩阵数量。
M(i,j)表示从第i+1个矩阵到第j+1个矩阵链的最少运算总量,故M(0,n-1)即表示了整个矩阵链的最少运算总量。
S(i,j)表示从第i+1个矩阵到第j+1个矩阵链的分割位置,因为每一矩阵链都可看作是两个矩阵链分别运算后的乘积。此矩阵可用于回溯矩阵相乘的顺序。
# 求最优值,记录备忘录
def MatrixChain(p):
n = len(p) - 1
# 存最优值
m = np.zeros((n, n))
# 存最优决策
s = np.zeros((n, n))
# R 表示矩阵链长度 从2开始
for r in range(2, n + 1):
# 从第i个矩阵Ai开始,长度为r,循环次数为n-r+1
for i in range(n - r + 1):
j = i + r - 1 # 当前矩阵端(Ai-Aj)起始为Ai 结尾为Aj
# 初始化
m[i][j] = m[i + 1][j] + p[i] * p[i + 1] * p[j + 1]
s[i][j] = i
# 比较寻最小值
for k in range(i + 1, j):
t = m[i][k] + m[k + 1][j] + p[i] * p[k + 1] * p[j + 1]
if t < m[i][j]:
m[i][j] = t
s[i][j] = k
return m, s
2.回溯
回溯函数如下,采用递归方法构造:
# 记录最优决策并构造最优解
res = []
def Traceback(i, j):
if i == j:
res.append('A' + str(i))
else:
res.append('(')
Traceback(i, int(s[i][j]))
Traceback(int(s[i][j] + 1), j)
res.append(')')
结果展示
矩阵链为:P=<50,35,25,10,60,70,3,5,10>
备忘录的值为
[[ 0. 43750. 26250. 56250. 103250. 23025. 23775. 24675.]
[ 0. 0. 8750. 29750. 75250. 17775. 18300. 18975.]
[ 0. 0. 0. 15000. 59500. 15150. 15525. 16050.]
[ 0. 0. 0. 0. 42000. 14400. 14550. 14850.]
[ 0. 0. 0. 0. 0. 12600. 13500. 14550.]
[ 0. 0. 0. 0. 0. 0. 1050. 2250.]
[ 0. 0. 0. 0. 0. 0. 0. 150.]
[ 0. 0. 0. 0. 0. 0. 0. 0.]]
可见最小值为24675.0
决策矩阵为:
[[0. 0. 0. 2. 2. 0. 5. 5.]
[0. 0. 1. 2. 2. 1. 5. 5.]
[0. 0. 0. 2. 2. 2. 5. 5.]
[0. 0. 0. 0. 3. 3. 5. 5.]
[0. 0. 0. 0. 0. 4. 5. 5.]
[0. 0. 0. 0. 0. 0. 5. 5.]
[0. 0. 0. 0. 0. 0. 0. 6.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
最终矩阵链的相乘顺序为:((A0(A1(A2(A3(A4A5)))))(A6A7))