動態規划—矩陣鏈乘法


矩陣鏈乘問題描述

給定n個矩陣構成的一個鏈<A1,A2,A3,.......An>,其中i=1,2,...n,矩陣A的維數為pi-1pi,對乘積 A1A2...A以一種最小化標量乘法次數的方式進行加全部括號。

注意:在矩陣鏈乘問題中,實際上並沒有把矩陣相乘,目的是確定一個具有最小代價的矩陣相乘順序。找出這樣一個結合順序使得相乘的代價最低。

動態規划分析過程

1)最優加全部括號的結構

  動態規划第一步是尋找一個最優的子結構。假設現在要計算AiAi+1....Aj的值,計算Ai...j過程當中肯定會存在某個k值(i<=k<j)將Ai...j分成兩部分,使得Ai...j的計算量最小。分成兩個子問題Ai...k和Ak+1...j,需要繼續遞歸尋找這兩個子問題的最優解。

  有分析可以到最優子結構為:假設AiAi+1....Aj的一個最優加全括號把乘積在Ak和Ak+1之間分開,則Ai..k和Ak+1..j也都是最優加全括號的。

2)一個遞歸解

  設m[i,j]為計算機矩陣Ai...j所需的標量乘法運算次數的最小值,對此計算A1..n的最小代價就是m[1,n]。現在需要來遞歸定義m[i,j],分兩種情況進行討論如下:

當i==j時:m[i,j] = 0,(此時只包含一個矩陣)

當i<j 時:從步驟1中需要尋找一個k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)。

3)計算最優代價

  雖然給出了遞歸解的過程,但是在實現的時候不采用遞歸實現,而是借助輔助空間,使用自底向上的表格進行實現。設矩陣Ai的維數為pi-1pi,i=1,2.....n。輸入序列為:p=<p0,p1,...pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代價,s[n][n]保存計算m[i,j]時取得最優代價處k的值,最后可以用s中的記錄構造一個最優解。書中給出了計算過程的偽代碼,摘錄如下:

MAXTRIX_CHAIN_ORDER(p)
 2   n = length[p]-1;
 3   for i=1 to n
 4       do m[i][i] = 0;
 5   for t = 2 to n  //t is the chain length
 6        do for i=1 to n-t+1
 7                      j=i+t-1;
 8                      m[i][j] = MAXLIMIT;
 9                      for k=i to j-1
10                             q = m[i][k] + m[k+1][i] + qi-1qkqj;
11                             if q < m[i][j]
12                                then m[i][j] = q;
13                                     s[i][j] = k;
14   return m and s;

 

MATRIX_CHAIN_ORDER具有循環嵌套,深度為3層,運行時間為O(n3)。如果采用遞歸進行實現,則需要指數級時間Ω(2n),因為中間有些重復計算。

4)構造一個最優解

第三步中已經計算出來最小代價,並保存了相關的記錄信息。因此只需對s表格進行遞歸調用展開既可以得到一個最優解。書中給出了偽代碼,摘錄如下:

 

PRINT_OPTIMAL_PARENS(s,i,j)
   if i== j 
      then print "Ai"
   else
      print "(";
      PRINT_OPTIMAL_PARENS(s,i,s[i][j]);
      PRINT_OPTIMAL_PARENS(s,s[i][j]+1,j);
      print")";

 

5)編程實現

public class Q1_Matrix_chain {
    public static int[] atest ={30,35,15,5,10,20,25};
    public static int[] a={3, 5, 2, 1, 10};
    public static int[] b={2, 7, 3, 6, 10};
    public static int[] c={10, 3, 15, 12, 7, 2};
    public static int[] d={7, 2, 4, 15, 20, 5};
    public static void main(String[] args)
    {
        System.out.println("<3, 5, 2, 1,10>");
        Matrix_Chain_Order(a);
        System.out.println("<2, 7, 3, 6, 10>");
        Matrix_Chain_Order(b);
        System.out.println("<10, 3, 15, 12, 7, 2>");
        Matrix_Chain_Order(c);
        System.out.println("<7, 2, 4, 15, 20, 5>");
        Matrix_Chain_Order(d);

    }

    public static void Matrix_Chain_Order(int[] a){
        int n = a.length-1;
        int[][] m = new int[n+1][n+1];
        int[][] s = new int[n+1][n+1];
        int i,j,k,t;

        for (i=0;i<=n;i++)
            m[i][i] = 0;
        for (i=0;i<=n;i++)
            s[i][i] = 0;
        for(t=2; t<=n; t++) //t is the chain length
        {
            for(i=1;i<=n-t+1;i++)//從第一矩陣開始計算,計算長度為t的最小代價
            {
                j = i+t-1;//長度為t時候的最后一個元素
                m[i][j] = 1000000;//初始化為最大代價
                for(k=i;k<=j-1;k++)//尋找最優的k值,使得分成兩部分k在i與j-1之間
                {
                    int temp = m[i][k]+m[k+1][j] + a[i-1]*a[k]*a[j];
                    if(temp < m[i][j])
                    {
                    m[i][j] = temp;   //記錄下當前的最小代價
                    s[i][j] = k;      //記錄當前的括號位置,即矩陣的編號
                    }
                }
            }
        }
        System.out.println("一個最優解為:");
        Display(s,1,n);
        System.out.println("\n計算的次數為:");
        System.out.println(m[1][n]);
    }
    public static void Display(int[][] s,int i,int j)
    {
        if( i == j)
        {
            System.out.print('A');
            System.out.print(i);
        }
        else
        {
            System.out.print('(');
            Display(s,i,s[i][j]);
            Display(s,s[i][j]+1,j);
            System.out.print(')');
        }

    }

}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM