算法13---動態規划矩陣鏈乘法
矩陣鏈乘法是動態規划里面使用到的一個例子
1 兩個矩陣的計算
那么對於一個矩陣的乘法,首先如果是兩個矩陣的乘法,那么如何實現呢?
注意到我們使用二維數組表示矩陣,但是二維數組不能作為函數的返回值。具體實現如下
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 5 #define a_rows 3 6 #define a_columns 4 7 #define b_rows 4 8 #define b_columns 3 9 10 void matrix_multiply(int a[a_rows][a_columns],int b[b_rows][b_columns],int c[a_rows][b_columns]) 11 { 12 if (a_columns!=b_rows) 13 { 14 printf("error!can't figure the answer!\n"); 15 } 16 for (int i = 0; i < a_rows; i++) 17 { 18 for (int j = 0; j < b_columns; j++) 19 { 20 c[i][j]=0; 21 for (int k = 0; k< a_columns; k++) 22 { 23 c[i][j]=c[i][j]+a[i][k]*b[k][j]; 24 } 25 } 26 } 27 } 28 29 int main() 30 { 31 32 printf("it is a easy matrix \n"); 33 34 int a[a_rows][a_columns]={{1,1,1,1},{2,2,2,2},{3,3,3,3}}; 35 int b[b_rows][b_columns]={{1,1,1},{2,2,2},{3,3,3},{4,4,4}}; 36 int c[a_rows][b_columns]={0}; 37 matrix_multiply(a,b,c); 38 for (int i = 0; i < 3; i++) 39 { 40 for (int j = 0; j < 3; j++) 41 { 42 printf("%d \n",c[i][j]); 43 if (j==2) 44 { 45 printf("\n"); 46 } 47 } 48 } 49 return 0; 50 }
2 矩陣鏈問題
問題描述
給定n個矩陣構成的一個鏈<A
1,A
2,A
3,.......A
n>,其中i=1,2,...n,矩陣A的維數為p
i-1p
i,對乘積 A
1A
2...A
n 以一種最小化標量乘法次數的方式進行加全部括號。
換句話說,就是在矩陣鏈乘問題中,實際上並沒有把矩陣相乘,目的是確定一個具有最小代價的矩陣相乘順序。找出這樣一個結合順序使得相乘的代價最低。
一般的過程如下
(1)最優括號話方案的結構特征
假設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)計算最優代價
我們不采用遞歸實現,而是自下向上的借助輔助空間保存中間量實現;
(4)構造一個最優解
具體的實現過程如下面所示
還沒有調好,主要是二維數組不能作為返回值
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define N 6 5 #define MAXVALUE 999999 6 7 void recursive_matrix_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1]); 8 int memoized_matrix_chain(int *p,int m[N+1][N+1],int s[N+1][N+1]); 9 int lookup_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1]); 10 11 //我們首先采用遞歸來實現 12 void recursive_matrix_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1]) 13 { 14 if (i==j) 15 { 16 m[i][j]=0; 17 } 18 else 19 { 20 int k; 21 m[i][j]=MAXVALUE; 22 for (int k = i; k < j; k++) 23 { 24 int temp=recursive_matrix_chain(p,i,k,m,s)+recursive_matrix_chain(p,k+1,j,m,s)+p[i-1]*p[k]*p[j]; 25 if (temp<m[i][j]) 26 { 27 m[i][j]=temp; 28 s[i][j]=k; 29 } 30 } 31 32 } 33 } 34 35 //因為遞歸的計算量太大,所以我們可以采用備忘錄的方法,自頂向下實現 36 37 int memoized_matrix_chain(int *p,int m[N+1][N+1],int s[N+1][N+1]) 38 { 39 40 for (int i = 1; i <=N; ++i) 41 { 42 for (int j = 0; j <=N; ++j) 43 { 44 m[i][j]=MAXVALUE; 45 } 46 } 47 return lookup_chain(p,1,N,m,s); 48 } 49 50 int lookup_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1]) 51 { 52 if (m[i][j]<MAXVALUE) 53 { 54 return m[i][j]; 55 } 56 if (i==j) 57 { 58 m[i][j]=0; 59 } 60 else 61 { 62 for (int k = i; i < j; ++k) 63 { 64 int temp=lookup_chain(p,i,k,m,s)+lookup_chain(p,k+1,j,m,s)+p[i-1]*p[k]*p[j]; 65 if (temp<m[i][j]) 66 { 67 s[i][j]=k; 68 } 69 } 70 } 71 return m[i][j]; 72 } 73 74 75 void print_optimal_parens(int s[N+1][N+1],int i,int j) 76 { 77 if (i==j) 78 { 79 printf("A%d\n",i); 80 } 81 else 82 { 83 printf("("); 84 print_optimal_parens(s,i,s[i][j]); 85 print_optimal_parens(s,s[i][j]+1,j); 86 printf(")"); 87 } 88 } 89 90 int main() 91 { 92 int p[N+1] = {30,35,15,5,10,20,25}; 93 int m[N+1][N+1]={0}; 94 int s[N+1][N+1]={0}; 95 int i,j; 96 memoized_matrix_chain(p,N+1,m,s); 97 printf("m value is: " ); 98 99 for(i=1;i<=N;++i) 100 { 101 for(j=1;j<=N;++j) 102 printf("%d ",m[i][j]); 103 printf("\n"); 104 printf("s value is: \n"); 105 106 for(i=1;i<=N;++i) 107 { 108 for(j=1;j<=N;++j) 109 printf("%d ",s[i][j]); 110 printf("\n"); 111 } 112 printf("the result is:\n"); 113 print_optimal_parents(s,1,N); 114 return 0; 115 }
現在再給出一個c++的版本,一個實現,我是看的別人的,自己可以修改
1 #include <iostream> 2 using namespace std; 3 4 #define N 6 5 #define MAXVALUE 1000000 6 7 void matrix_chain_order(int *p,int len,int m[N+1][N+1],int s[N+1][N+1]); 8 void print_optimal_parents(int s[N+1][N+1],int i,int j); 9 10 int main() 11 { 12 int p[N+1] = {30,35,15,5,10,20,25}; 13 int m[N+1][N+1]={0}; 14 int s[N+1][N+1]={0}; 15 int i,j; 16 matrix_chain_order(p,N+1,m,s); 17 cout<<"m value is: "<<endl; 18 for(i=1;i<=N;++i) 19 { 20 for(j=1;j<=N;++j) 21 cout<<m[i][j]<<" "; 22 cout<<endl; 23 } 24 cout<<"s value is: "<<endl; 25 for(i=1;i<=N;++i) 26 { 27 for(j=1;j<=N;++j) 28 cout<<s[i][j]<<" "; 29 cout<<endl; 30 } 31 cout<<"The result is:"<<endl; 32 print_optimal_parents(s,1,N); 33 return 0; 34 } 35 36 void matrix_chain_order(int *p,int len,int m[N+1][N+1],int s[N+1][N+1]) 37 { 38 int i,j,k,t; 39 for(i=0;i<=N;++i) 40 m[i][i] = 0; 41 for(t=2;t<=N;t++) //當前鏈乘矩陣的長度 42 { 43 for(i=1;i<=N-t+1;i++) //從第一矩陣開始算起,計算長度為t的最少代價 44 { 45 j=i+t-1;//長度為t時候的最后一個元素 46 m[i][j] = MAXVALUE; //初始化為最大代價 47 for(k=i;k<=j-1;k++) //尋找最優的k值,使得分成兩部分k在i與j-1之間 48 { 49 int temp = m[i][k]+m[k+1][j] + p[i-1]*p[k]*p[j]; 50 if(temp < m[i][j]) 51 { 52 m[i][j] = temp; //記錄下當前的最小代價 53 s[i][j] = k; //記錄當前的括號位置,即矩陣的編號 54 } 55 } 56 } 57 } 58 } 59 60 //s中存放着括號當前的位置 61 void print_optimal_parents(int s[N+1][N+1],int i,int j) 62 { 63 if( i == j) 64 cout<<"A"<<i; 65 else 66 { 67 cout<<"("; 68 print_optimal_parents(s,i,s[i][j]); 69 print_optimal_parents(s,s[i][j]+1,j); 70 cout<<")"; 71 } 72 73 }
