矩陣鏈乘積演算法
Matrix Chain Multiplication or Matrix Chain Ordering Problem
純量乘法數
假設
M是一個xy的矩陣
N是一個yz的矩陣
MN需要xyz次純量乘法計算(xz個元素,每個元素執行y次乘法)
矩陣乘法先後次序會影響次數
定義 矩陣鏈乘積問題
一個含n個矩陣的矩陣鏈乘積: M1M2…Mn
找出一種完全括號(Fully Parenthesized)方式
使用最少純量乘法求出乘積
完全括號:單一矩陣或兩個完全括號矩陣鏈乘積相乘並加括號
EX: X、Y、(XY)
窮舉失敗: 窮舉所有不同的完全括號方式
長度為n的矩陣鏈乘積不同的完全括號方式的總數P(n):
P(n)為卡塔蘭數(Catalan number)=Cn-1 =O(2n)
因需要太多計算所以無法暴力解
=> 使用動態規劃演算法
動態規劃: 遞迴關係
動態規劃: 建構表格
長度為n的矩陣鏈乘積,
表格m[1~n, 1~n],
m[i,j]=Mi~Mj所需的最小純量相乘數
目標求得m[1,n]=M1~Mn的相乘數
輔助表格s[1~n, 1~n] 記錄分割位置k
s[i,j]紀錄哪一個分割位置k造就了m[i,j]的最小純量相乘數
動態規劃: 填表
邊界條件為i=j
以
11,22,…,nn, //d=j-i=0 都填0
12,23,…,(n-1)n, // d=j-i=1
13,24,(n-2)n, // d=j-i=2
…,
1n // d=j-i=n-1
的方式填表
(由底部而上的方式)
演算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Algorithm 矩陣鏈乘積演算法 Input: 矩陣鏈乘積 e(1)~e(n) Output: 所需最小乘法數 m[1,n] for i = 1 to n m[i,i] = 0 for d = 1 to n-1 do for i = 1 to n-d j = i+d m[i,j] = 無限大 for k = i to j-1 tmp = m[i,k] + m[k+1,j] + e(i-1)e(k)e(j) if tmp < m[i,j] m[i,j] = tmp s[i,j] = k return m and s
|
時間複雜度
三層for每層都是O(n)
=> O(n3)
印出最佳完全括號矩陣鏈乘積演算法
1 2 3 4 5 6 7 8
| Algorithm PRINT_OPTIMAL_PARENS(s,i,j) if i=j print "M" + i else print "(" PRINT_OPTIMAL_PARENS(s,i,s[i,j]) PRINT_OPTIMAL_PARENS(s,s[i,j]+1,j) print ")"
|
ex: (M1(M2M3))
實際範例
C++ 參考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <iostream> using namespace std; void PRINT_OPTIMAL_PARENS(int** s, int i, int j) { if (i == j) { cout << "A" << i; } else { cout << "("; PRINT_OPTIMAL_PARENS(s, i, s[i][j]); PRINT_OPTIMAL_PARENS(s, s[i][j] + 1, j); cout << ")"; } } void MatrixChainOrder(int* e, int n) { int **m; m = new int*[n + 1]; for (int i = 0; i <= n; ++i) { m[i] = new int[n + 1]; } int **OPTIMAL_PARENS; OPTIMAL_PARENS = new int*[n + 1]; for (int i = 0; i <= n; ++i) { OPTIMAL_PARENS[i] = new int[n + 1]; } for (int i = 1; i <= n; i++) { m[i][i] = 0; } for (int d = 1; d <= n-1; d++) { for (int i = 1; i <= n-d; i++) { int j = i + d; m[i][j] = 2147483647; for (int k = i; k <= j-1; k++) { int tmp = m[i][k] + m[k + 1][j] + e[i-1] * e[k] * e[j]; if (tmp < m[i][j]) { m[i][j] = tmp; OPTIMAL_PARENS[i][j] = k; } } } } PRINT_OPTIMAL_PARENS(OPTIMAL_PARENS, 1, n); cout << endl; cout << m[1][n]; } int main() { int num = 6; int *row_col = new int[num + 1]; int test_data[7] = { 30, 35, 15, 5, 10, 20, 25 }; for (int i = 0; i < num + 1; i++) { row_col[i] = test_data[i]; } MatrixChainOrder(row_col, num); system("pause"); return 0; }
|