[动态规划] 矩阵链乘法

讲解:n 个矩阵相乘时,M_{i}p_{i - 1}p_{i} 列的矩阵,以 (M_{1}M_{2}....M_{6}) 为例进行分析,这些矩阵的乘积有多种计算顺序。举个例子,我们按习惯的从左到右的顺序进行计算时可以写作 (((((M_{1}M_{2})M_{3})M_{4})M_{5})M_{6}) ,从左到右计算时可以写作 (M_{1}(M_{2}(M_{3}(M_{4}(M_{5}M_{6}))))) 。除此之外还有 (M_{1}(M_{2}(M_{3}M_{4})(M_{5}M_{6}))) 等等,计算的顺序多种多样。这些计算顺序得出的结果(矩阵链乘积)完全相同,但不同顺序下的 “ 乘法运算次数 ” 会有所差异。

处理矩阵链乘法问题时,如果检查所有可能的计算顺序,那么算法的复杂度将达到 O(n!) 。不过,由于这个问题能够分割成更小的局部问题,我们可以运用动态规划法。

首先,(M_{1}M_{2}) 只有一种计算方法(顺序),需要 p_{0}\,\ast\,p_{1}\, \ast \, p_{2} 次乘法运算。同理,(M_{2}M_{3}) 也只有一种计算方法,需要 p_{1}\, \ast \, p_{2}\, \ast \, p_{3} 次乘法运算。归纳后可知,(M_{i}M_{i + 1}) 只有一种计算方法,需要 p_{i - 1}\, \ast \, p_{i}\, \ast \, p_{i+1} 次乘法运算。于是我们将这些运算次数视为 “ 成本 ” 记录在表中。

接下来求 (M_{1}M_{2}M_{3}),\,(M_{2}M_{3}M_{4})\,,....,\,(M_{n - 2}M_{n-1}M_{n}) 的最优计算方法。举个例子,计算 (M_{1}M_{2}M_{3}) 的最优计算方法时,我们要分别算出 (M_{1}(M_{2}M_{3}))((M_{1}M_{2})M_{3}) 的成本,取其中最小的一个作为 (M_{1}M_{2}M_{3}) 的成本记录在表中。这里:

  • (M_{1}(M_{2}M_{3})) 的成本 =(M_{1}) 的成本 +(M_{2}M_{3}) 的成本 +p_{0}\,\ast\,p_{1}\,\ast\,p_{3}
  • ((M_{1}M_{2})M_{3}) 的成本 =(M_{2}M_{3}) 的成本 + (M_{3}) 的成本 +\,p_{0}\,\ast\,p_{2}\,\ast\,p_{3}

请注意,这一步用到的 (M_{1}M_{2})(M_{2}M_{3}) 的成本可以直接从表中引用,不需要再进行计算。另外还要注意,当 1\leqslant i \leqslant n 时,(M_{i}) 的成本为 0

一般情况下,矩阵链乘法 (M_{i}M_{i+1}.....M_{j}) 的最优解就是 (M_{i}M_{i+1}...M_{k})(M_{k+1}...M_{j}) 的最小成本(其中 i \leqslant k < j)。

举个例子,(M_{1}M_{2}M_{3}M_{4}M_{5})(i = 1,\,j = 5 ) 时的最优解就是下列式子中的最小值。

  • (M_{1})(M_{2}M_{3}M_{4}M_{5}) 的成本 =(M_{1}) 的成本 +(M_{2}M_{3}M_{4}M_{5}) 的成本 +\, p_{0}\,\ast\,p_{1}\,\ast\,p_{5}(k = 1)
  • (M_{1}M_{2})(M_{3}M_{4}M_{5}) 的成本 =(M_{1}M_{2}) 的成本 +\,(M_{3}M_{4}M_{5}) 的成本 +\,p_{0}\,\ast\,p_{2}\,\ast\,p_{5} \,(k = 2)
  • (M_{1}M_{2}M_{3})(M_{4}M_{5}) 的成本 =\,(M_{1}M_{2}M_{3}) 的成本 +\,(M_{4}M_{5}) 的成本 +\,p_{0}\,\ast\,p_{3}\,\ast\,p_{5}\,(k = 3)
  • (M_{1}M_{2}M_{3}M_{4})(M_{5}) 的成本 =\,(M_{1}M_{2}M_{3}M_{4}) 的成本 +\,(M_{5}) 的成本 +\,p_{0}\,\ast\,p_{4}\,\ast\,p_{5}(k = 4)

现在来看看这个算法的具体实现方法。先准备下述变量。

m[n+1][n+1] 该二维数组中,m[i][j] 表示计算 (M_{i}M_{i+1}...M_{j}) 时所需要乘法运算的最小次数
p[n+1] 该一位数组用于储存矩阵的行列数,其中 M_{i}p[i - 1] \,\ast\,p[i] 的矩阵

利用上述变量,我们可以利用下面的式子求出 m[i][j]

m[i][j] = \left\{\begin{matrix} 0& & (if \, i = j)\\ min_{i\leqslant k<j}(m[i][k] + m[k+1][j] + p[i - 1]\,\ast\,p[k]\,\ast\,p[j])& & (if\,i <j) \end{matrix}\right.

这个算法的实现方法如下:

matrixChainMultiplication()
    for i = 1 to n
        m[i][j] = 0

    for l = 2 to n
        for i = 1 to n - l + 1
            j = i + l - 1
            m[i][j] = INFTY
            for k = i to j - 1
                m[i][j] = min(m[i][j], m[i][k] + m[k + l][j] + p[i - l] * p[k] * p[j])

考察:这个算法需要让对象的矩阵对象的数量 l2 逐步增加到 n,同时对于每个 l 要通过不断改变 ij 来改变指定范围。除此之外,还需要在 ij 的范围内让 k 不断变化。整个算法由三重循环构成,因此复杂度为 O(n^{3})

#include <iostream>
#include <algorithm>
using namespace std;

static const int N = 100;

int main() {
    int n, p[N + 1], m[N + 1][N + 1];
    cin >> n;
    
    for (int i = 1; i <= n; i++)
        cin >> p[i - 1] >> p[i];

    for (int i = 1; i <= n; i++) m[i][j] = 0;
    for (int l = 2; l <= n; l++) {
        for (int i = 1; i <= n - l + 1; i++) {
            int j = i + l - 1;
            m[i][j] = (1 << 21);
            for (int k = i; k <= j - 1; k++)
                m[i][j] = min(m[i][j], m[i][k] + m[k + l][j] + p[i - l] * p[k] * p[j]);
        }
    }

    cout << m[l][n] << endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/laugh12321/article/details/81566004
今日推荐