1. 程式人生 > >MCM(矩陣鏈乘法)

MCM(矩陣鏈乘法)

ret for das amp 就是 得到 int sub 決定

  這是《算法導論》動態規劃中的一個問題。問題簡述如下:我們在求解矩陣相乘時通常會有一個最優括號方案來對矩陣進行順序相乘,這樣會減少大量的計算時間。

  我們知道矩陣A*B相乘,只能是當矩陣A的列數等於矩陣B的行數時才能進行相乘,且假設A·B = C,A為p×q規模的矩陣,B為q×r的矩陣,則乘積C的規模為p×r的矩陣。計算A·B所需的時間由乘法次數決定,即pqr。

  例如:有三個矩陣的規模分別為:A = 10×100,B = 100×5,C = 5×50。如果按順序(AB)C計算,則需要10×100×5 + 10×5×50 = 7500次乘法,如果按順序A(BC)計算,則需要100×5×50 + 10×100×50 = 75000次乘法...所以,按第一種順序計算矩陣鏈乘積要比第二種快非常多。

  矩陣鏈乘法問題可描述如下:給定n個矩陣的鏈<A1,A2,...,An>,矩陣Ai的規模為pi - 1×pi(1 ≤ i ≤ n),求完全括號化方案,使得計算乘積A1A2...An所需標量乘法次數最少。

  分析問題描述可知道,我們假設在矩陣鏈乘中找到一個合適的切割點進行括號化,可得到問題的最優括號方案,然後對切割點的兩邊的子問題用相同的方法獨立求解,不斷進行下去,則最終將得到原問題的最優解。這就是該問題的最優子結構。

  構造遞歸公式:令m[i][j] 表示計算矩陣Ai...j所需標量乘法次數的最小值,那麽,原問題的最優解 —— 計算Ai...n

所需的最低代價就是m[1][n]。

  對於i = j 時,矩陣鏈只包含唯一的矩陣 Ai...i = Ai ,不需要做任何標量乘法運算。若 i < j,利用最優子結構來計算m[i][j]。如前面假設Ai,Ai + 1,...,Aj的最優括號化方案的分割點在矩陣的Ak和Ak + 1之間,其中i ≤ k < j。那麽,m[i][j]就等於計算Ai...k和Ak + 1...j的代價加上兩者相乘的代價的最小值。由於矩陣Ai的大小為pi×pi + 1,易知Ak + 1...j相乘的代價為pi·pk + 1·pj + 1次標量乘法運算。因此,得到:

m[i][j] = m[i][k] + m[k + 1][j] + p[i]·p[k + 1]·p[j + 1]

  由於k值取法只有 j - i 種可能的取值,即k = i, i + 1, ..., j。檢查所有可能的情況,找到最優,因此遞歸公式變為:

           (    0,                 i = j;
m[i][j] = {    
           (    min(m[i][k] + m[k + 1][j] + p[i]·p[k + 1]·p[j + 1]),    i < j.

  O(n^3)叠代解法:

#include <iostream>
#include <vector>

class Solution {
public:
    std::vector<std::vector<int> > MatrixChainOrder(std::vector<int>& p)
    {
        const int n = p.size() - 1;
        std::vector<std::vector<int> > m(n, std::vector<int> (n));

        for(int i = 0; i < n; i++)
        {
            m[i][i] = 0;
        }
        for(int l = 1; l < n; l++)
        {
            for(int i = 0; i < n - l; i++)
            {
                int j = i + l;
                m[i][j] = INT_MAX - 1;
                for(int k = i; k < j; k++)
                {
                    int q = m[i][k] + m[k + 1][j] + p[i]*p[k + 1]*p[j + 1];
                    if(q < m[i][j])
                    {
                        m[i][j] = q;
                    }
                }
            }
        }

        return m;
    }
};

int main()
{
    std::vector<int> p{30, 35, 15, 5, 10, 20, 25};
    Solution solve;
    std::vector<std::vector<int> > res = solve.MatrixChainOrder(p);

    for(int i = res.size() - 1; i >= 0; i--)
    {
        for(int j = 0; j <= i; j++)
            std::cout << res[j][i] << ‘ ‘;
        std::cout << std::endl;
    }

    return 0;
}

  遞歸版感覺有點問題...所以再研究一段時間。

  參考資料:以上皆為《算法導論》 — 動態規劃原文

  

MCM(矩陣鏈乘法)