矩陣連乘問題(內附動態規划算法代碼)


矩陣連乘問題

若矩陣A是一個p*q的矩陣,B是一個q*r的矩陣,則C=AB,是一個p*r的矩陣,需進行pqr次數乘計算。

存在{A1,A2,A3}三個矩陣,維數分別為100*5,5*50,50*10。若直接相乘,A1*A2*A3,則需要進行n=100*5*50+100*50*10=25000+50000=75000次數乘計算。如果我們調整運算順序,A1*(A2*A3),則需要進行n=5*50*10+100*5*10=2500+5000=7500次數乘計算。

由此可見,當進行矩陣連乘運算時,加括號的方式,即計算次序對計算量有很大的影響。

 

 

代碼展示:

 1 #include<iostream>
 2 
 3 using namespace std;
 4 /*
 5 自底向上的推出矩陣連乘的最優解
 6 先從兩個矩陣相乘開始,而后三個矩陣相乘,四個......直到推出目標長度的最優解 ,即假設一個矩陣鏈,初始長度為2,算出所有相鄰矩陣相乘的計算次數,而后使其長度為3...4...直到目標長度
 7 狀態轉移方程:
 8  m[i][j]=min {m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]}   i<=k<j  i<j
 9  m[i][j]=0   i==j;
10 */
11 #define LEN 5  //矩陣個數 
12 //矩陣連乘函數,找到最優解 
13 void MatrixChain(int *p, int m[][LEN + 1], int s[][LEN + 1]) {
14     for (int i = 0; i < LEN + 1; i++)    m[i][i] = 0;      //初始化,對角線元素置零,即當矩陣鏈長度為1時(只有一個矩陣)不用乘,為零 
15     for (int r = 2; r <= LEN; r++) {  //r表示矩陣鏈的長度,從2開始,兩個矩陣相乘,而后3...4...5... 
16         for (int i = 1; i <= LEN - r + 1; i++) {    //i是矩陣鏈的首個矩陣,小於矩陣個數減矩陣鏈長度加一 
17             int j = i + r - 1;    //j是矩陣鏈的最后一個元素 
18             m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j];   //m[i][j]是子結構,從最左邊開始推 
19             s[i][j] = i;  //標記斷開的位置 
20             for (int k = i + 1; k < j; k++) {    //k是i和j直接的斷開點,是在i和j之間的子結構 ,通過k的循環找到最優的解 
21                 int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];   //狀態轉移方程 
22                 if (t < m[i][j]) {
23                     m[i][j] = t;    //更新最優解 
24                     s[i][j] = k;    //更新斷開點 
25                 }
26             }
27         }
28     }
29 }
30 
31 //回溯函數,根據s[i][j]數組標記的位置,回溯找到斷開的位置 
32 void Traceback(int i, int j, int s[][LEN + 1]) {
33     if (i == j) {    //當i與j相等 說明回溯到該矩陣的位置了
34         cout << "A" << i;
35     }
36     else {
37         cout << "(";
38         Traceback(i, s[i][j], s);    //從尾往頭回溯
39         Traceback(s[i][j] + 1, j, s);    //從斷點往后回溯
40         cout << ")";
41     }
42 }
43 //輸出函數 
44 void output(int t[][LEN + 1]) {
45     for (int i = 1; i <= LEN; i++) {
46         for (int j = 1; j <= LEN; j++) {
47             cout << " " << t[i][j] << " ";
48         }
49         cout << endl;
50     }
51 }
52 int main(void) {
53     int p[LEN + 1] = { 6,8,9,3,4,10 }; //矩陣的維度分別是2*3,3*4,4*5,5*6,6*7,LEN+1個數表示LEN個矩陣 
54     int m[LEN + 1][LEN + 1] = { 0 };    //記錄最優子結構的二維數組 
55     int s[LEN + 1][LEN + 1] = { 0 };    //記錄最優解對應的括號的位置     
56 
57     MatrixChain(p, m, s);
58 
59     cout << endl;
60     output(m);
61     cout << endl;
62     output(s);
63     cout << endl;
64     cout << "outcome:" <<endl;
65     Traceback(1, LEN, s);
66     cout << endl;
67 
68     return 0;
69 }

 運行結果:

 

與備忘錄方法的區別:

我們使用的動態規划方法中其實融入了備忘錄的一些東西,我們的m和s數組都是用來記錄的,所以備忘錄方法與我們使用的方法類似,不同在於,我們是自底向上的,而備忘錄方法是自頂向下的進行。


免責聲明!

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



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