首页 > 代码库 > [CLRS][CH 15.2] 动态规划之矩阵链乘法
[CLRS][CH 15.2] 动态规划之矩阵链乘法
摘要
整理了矩阵链乘法的动态规划思路。
题目
给定n个要相乘的矩阵构成的序列<A1, A2, ... , An>,其中 i=1, 2, ..., n,矩阵 Ai 的维数为pi-1*pi。计算乘积 A1A2...An 的最小代价的矩阵相乘循序。
补充:矩阵乘法满足结合律,例如,乘积 A1A2A3A4 共有五种不同加括号结合形式。不同的结合形式极大的影响运算效率。当且仅当矩阵A和B相容(A.列 = B.行)时,才可以计算矩阵乘法。例如:矩阵A为p*q, 矩阵B为q*r,则相乘后得到的矩阵C为p*r。计算C的时间为pqr。下面给出矩阵相乘计算伪代码:
; 计算矩阵A, B乘积, rows和columns分别为行和列MATRIX-MULTIPLY(A, B)if (columns[A] != rows[B]) error "Incompatible Dimensions."else { for i = (1 to rows[A]) for j = (1 to columns[B]) C[i, j] = 0; for k = (1 to columns[A]) C[i, j] = C[i, j] + A[i, k] * B[k, j] return C}
思路
计算全部括号的重数
设P(n)表示一串n个矩阵可能的全部加括号方案数。当n=1时,只有一个矩阵,因此只有一种方案;当n>=2时,一个加全部括号的矩阵乘积,就是两个加全部括号的矩阵子乘积的乘积。而且这两个子乘积之间的分裂可能发生在第k个和第(k+1)个矩阵之间,其中k = 1, 2, ..., n-1。因此可得递归式:
$P(n)= \begin{cases} 1 & n=1\\ \sum_{k=1}^{n-1}{P(k)P(n-k)} & n\geqslant2 \end{cases}$
步骤1: 最优加全部括号的结构
动态规划的第一步就是找最优子结构,然后利用子问题最优解构造原问题最优解。对于本体而言,最优子结构如下:
假设 A1Ai+1...Aj 的一个最优加全部括号形式把乘积在 AkAk+1 之间分开,则对于原式 A1Ai+1...Aj 的最优加全部括号的前半部分 A1Ai+1...Ak 的加全部括号形式必须是 A1Ai+1...Ak 的一个最优解。后半部分同理。
因此我们得到最优子结构构造最优解。已经得出:一个矩阵链乘法的最优解需要分割乘积,因此可以讲原问题分割成两个子问题寻找最优解,最后合并。必须保证在寻找一个正确的位置分割时,已经考虑过所有可能的位置,从而确保已检查过了最优的解法。
步骤2: 一个递归解
设 m[i, j] 为计算矩阵 Ai..j 的最优解,即所需标量乘法次数最小值,对于整个问题,最优解即为 m[1, n]。易得对于乘积 Ai..j 递归方程:
$\textrm{m}[i,j]=\begin{cases}0&i=j\\\min_{i\leq k<j}(\textrm{m}[i,k]+\textrm{m}[k+1,j]+p_{i-i}p_{k}p_{j})&i<j\end{cases}$
为了追踪如何构造最优解,定义 s[i,j] 为这样一个k值,在该处分裂乘积后可得一个最优解。亦即 s[i,j] 等于使 $\textrm{m}[i,j]=\textrm{m}[i,k]+\textrm{m}[k+1,j]+p_{i-1}p_{k}p_{j}$ 的k值。
步骤3: 计算最优代价
我们使用自底向上的表格法来计算最优代价。下面的代码中包括如下数据:
设矩阵 Ai 的维数为 pi-1*pi , i = 1, 2, ..., n。
输入序列为 p = <p0, p1, ... , pn>,其中 length[p] = n + 1。
使用一个辅助表 m[1..n, 1..n] 来保存 m[i, j] 的代价。
使用一个辅助表 s[1..n, 1..n] 来计算 m[i, j] 时取得最优代价处k的值。利用表格s来构造一个最优解。
为了正确实现自底向上,必须确定哪些表项用来计算 m[i, j] 。计算 m[i, j] 的公式说明计算一个有 j-i+1 个矩阵的矩阵链乘积时,代价 m[i, j] 仅依赖于计算一个有少于 j-i+1 个矩阵的矩阵链乘积的代价。也就是说,对于 k = i, i+1, i+2, ..., j-1,矩阵 Ai..k 是 k-i+1 < j-i+1 个矩阵的乘积,矩阵 Ak+1..j 是 j-k < j-i+1 个矩阵的乘积。因此,该算法填表m的方式对应于求解按长度递增的矩阵链上的家全部括号问题。
MATRIC-CHAIN-ORDER(p)n = length[p] - 1for i = (1 to n) ; 该循环令 m[i,i] = 0, 即长度为1的链的最小代价 m[i, i] = 0for l = (2 to n) ; l为链的长度, 循环计算长度为(2..n)的链的最小代价 for i = (1 to n-l+1) m[i, j] = ∞ for k = (i to j-1) 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] = kreturn m and s
[CLRS][CH 15.2] 动态规划之矩阵链乘法