1. 程式人生 > >矩陣鏈乘問題

矩陣鏈乘問題

down -m turn 給定 code 需要 ... mat OS

矩陣鏈乘問題

應用動態規劃

給定n個矩陣的序列Ai , 計算A1A2A3*...An. 矩陣乘法滿足結合律,乘法的計算順序不同,需要計算的次數就不同,

  • 假設A1是pq的矩陣A2是qr的矩陣,那麽計算A1A2的結果需要計算pq*r次
  • 計算A1A2A3 A1(10100) A2(1005) A3(550) 如果按照(A1A2)A3計算,需要計算次數是101005 + 105*50 = 7500次
  • 如果按照A1(A2A3)計算,需要計算次數是 100550 + 1010050 = 75000次
  • 不同的計算順序計算效率可能會差10倍

矩陣鏈乘可以描述為:給定n個矩陣的鏈,給定一個括號化方案,使得矩陣乘法次數最少

動態規劃的第一步是確定最優子結構

對Ai....Aj求最佳括號化,必然要在中間加一個分割點,原序列轉化為兩個子序列Ai..Ak , Ak+1..Aj
設d[i,j]表示計算鏈i~j所需的最小代價
原問題的代價 = Ai..Ak 的代價 + Ak+1..Aj + 兩者相乘的代價
d[i][j] = d[i][k] + d[k+1][j] + p[k-1] * p[k] * p[k+1] k in (i,j)
如何利用最優子結構的性質 從子問題的最優解構造出原問題的最優解?
每個子問題的求解都要劃分鏈,找到子問題最優的劃分點,然後將子問題的最優解合並起來。
設s[i,j]記錄子鏈i~j的最優劃分點的位置,那麽s[1,n]將鏈劃分為兩個子鏈,如此遞歸的劃分,就得到了原問題的最優劃分方案,

狀態轉移代碼

void MATRIX_CHAIN_ORDER()
{
    for(int i=1;i<=n;i++) 
        d[i][i] = 0;
    
    for(int l=1;l<n;l++)
    {
        for(int i=1;i+l<=n;i++)
        {
            int j = i+l;
            d[i][j] = INF;
            for(int k =i;k<j;k++)
            {
                int mid = d[i][k] + d[k+1
][j] + p[k-1]*p[k]*p[k+1]; if(mid<d[i][j]) { d[i][j] = mid; s[i][j] = k; } } } } }

如此,計算出了每個區間的最優劃分點。時間復雜度O(n^3)

最後一步:構造最優解

根據每個區間的最優劃分點還原出結果

void mer(int x,int y)
{
    if(x>y)return;
    if(x==y)printf("A%d",x);
    else {
        printf("(");
        mer(x,s[x][y]);
        printf("*");
        mer(s[x][y]+1,y);
        printf(")");
    }
}

矩陣鏈乘問題