[動態規劃] 矩陣鏈乘法

講解: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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章