算法設計——矩陣連乘問題


白天什么也沒學,晚上才終於拿着筆,對着代碼,寫寫畫畫,終於看明白是怎么計算的了。


 

以這6個矩陣連乘作為例子

A1 A2 A3 A4 A5 A6
30*35 35*15 15*5 5*10 10*20 20*25

 

 

 

1 首先,要明白兩個矩陣相乘所需要做的乘法次數:

2 由於連乘的矩陣必須滿足,前一個矩陣的列數=后一個矩陣的行數,所以可以使用一個數組來存儲連乘矩陣的行列數:

p[7]={30,35,15,5,10,20,25};

3 分析最優解的結構:

A[i:j]表示矩陣i到矩陣j的連乘,那么A[i:j]=A[i:k]+A[k+1:j]+p[i-1]*p[k]*p[j](這兩個矩陣相乘所需的乘法次數);

如果A[i:j]的所需要的乘法次數是最少的,那么A[i:k]和A[k+1:j]所需要的乘法次數也應該是最少的,否則A[i:j]就不是最優的,所以滿足最優子結構性質;

4 建立遞歸關系:

m[i][j]表示矩陣i連乘到矩陣j的最少乘次數,那么原問題的最優值是m[1,n];

當i=j時,為單個矩陣,m[i][i]=0;

當i<j時,利用最優子結構性質來計算;

獲得以下遞歸定義: 

m[i][j]=0;    i=j

m[i][j]=mini<=k<j{m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]};  i<j

如果需要得出最優解的加括號結果,需要另一個二維數組s[i][j],用於記錄使m[i][j]獲得最優解的斷點,k值;

5 分析代碼

 1 int m[8][8]={0},s[8][8]={0};
 2 
 3 void MaxtrixChain(int *p,int n){
 4     for(int i=1;i<=n;i++) m[i][i]=0;
 5     for(int r=2;r<=n;r++)
 6         for(int i=1;i<=n-r+1;i++){
 7             int j=i+r-1;
 8             m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
 9             s[i][j]=i;
10             
11             for(int k=i+1;k<j;k++){
12                 int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
13                 if(t<m[i][j]){
14                     m[i][j]=t;
15                     s[i][j]=k;
16                 }
17             }
18         }
19 }

書上寫了“依據其遞歸式自底向上的方式進行計算”,沒拿出筆畫之前,我真的沒看懂這句話到底是怎么自底向上的,於是我要開始寫寫畫畫了;

我們先暫時只看下面這部分代碼:

void MaxtrixChain(int *p,int n){
    for(int i=1;i<=n;i++) m[i][i]=0;
    for(int r=2;r<=n;r++){
        for(int i=1;i<=n-r+1;i++){
            int j=i+r-1;
            m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
        }
} }

 

具體用數據分析一下

把右圖補充完整,將得到以下結果:這是我理解的自底向上地計算;

接下來看另一部分代碼:

    for(int r=2;r<=n;r++){
        for(int i=1;i<=n-r+1;i++){
            int j=i+r-1;
            
            for(int k=i+1;k<j;k++){
                int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                if(t<m[i][j]){
                    m[i][j]=t;
                    s[i][j]=k;
                }
            }
        }
    }

所以我們需要把這兩部分代碼結合起來使用:

那么對於m[1][4]我們是枚舉了所有加括號的可能情況,並存儲了其中最小的一個,獲得了最優值;

那么對於m[2][5]、m[3][6]也能得到最優值;並且沒有做任何重復計算;

當r=5時,m[1][5]、m[2][6]也能得到最優值;

當r=6時,m]1][6]也能得到最優值;如此就得到了原問題的最優解;

 每一次得到更小值的時候,s[i][j]=k,記錄(更新)這個斷點情況;最后可以用來幫助構造最優解的加括號形式;

6 以下給出完整源代碼:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 //矩陣連乘:完全加括號問題
 5 //A1(p1*p2),A2(p2*p3),兩個矩陣相乘,總乘法次數為p1*p2*p3,其中p2表示結果的每個元素所需要的乘法次數
 6 //m[i][j]表示,第i個矩陣到第j個矩陣的連乘,所需要的乘法次數
 7 //對於多個矩陣連乘,可以得出以下遞推式
 8 //m[i][j]=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]
 9 //問題是如何找到這個k,使乘法次數最少
10 int m[7][7]={0};    //m[i][j]用於存儲,第i個矩陣連乘到第j個矩陣的乘法次數,那么m[1][n]為最終結果
11 int s[7][7]={0};    //s[i][j]用於存儲,矩陣i和矩陣j之間所取的斷點,k值,用於構造結果的加括號情況
12 
13 void MatrixChain(int n,int p[]){
14     for(int i=1;i<=n;i++)   m[i][i]=0;      //單個矩陣乘法次數為0
15     
16     //采用自底向上的方式實現遞推式
17     for(int r=2;r<=n;r++){       //表示r個矩陣的連乘
18         for(int i=1;i<=n-r+1;i++){  //從第i個矩陣開始,作為連乘的起點,有r個矩陣連乘,所以要<=n-r+1
19             int j=i+r-1;        //j-i=r-1,表示j是r個矩陣連乘中的終點
20             m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j]; 
21                 //相當於m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j],顯然m[i][i]=0
22                 //即表示Ai……Aj=Ai(Ai+1……Aj),從最后一個開始加括號
23             s[i][j]=i;
24             
25             for(int k=i+1;k<j;k++){
26                 int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];   //在i~j中尋找k
27                 if(t<m[i][j]){
28                     m[i][j]=t;      //記錄t歷史上的最小值,最終結果即為連乘中的最優值
29                     s[i][j]=k;      //記錄斷點k值
30                 }
31             }
32 
33         }
34     }
35 }
36 
37 //利用s[i][j]進行構造連乘的加括號情況
38 void TraceBack(int i,int j){
39     if(i==j) return;
40     TraceBack(i,s[i][j]);   //以s[i][j]記錄的k為分界點,找左邊,i~k
41     TraceBack(s[i][j]+1,j); //找右邊,k+1~j
42     cout<<"Matrix A"<<i<<","<<s[i][j];
43     cout<<" and A"<<s[i][j]+1<<","<<j<<endl;
44 }
45 
46 int main(){
47     int arr[7]={30,35,15,5,10,20,25};
48     int n=6;
49     MatrixChain(n,arr);
50     TraceBack(1,n);
51     cout<<m[1][n]<<endl;
52     system("pause");
53     return 0;
54 }

 

哇本渣終於弄清楚矩陣連乘了!!


免責聲明!

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



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