矩陣連乘

給定n個矩陣:A1,A2,...,An,其中Ai與Ai+1是可乘的,i=1,2...,n-1。確定計算矩陣連乘積的計算次序,使得依此次序計算矩陣連乘積需要的數乘次數最少。輸入數據爲矩陣個數和每個矩陣規模,輸出結果爲計算矩陣連乘積的計算次序和最少數乘次數。這種計算次序可以用加括號的方式來確定。若一個矩陣連乘積的計算次序完全確定,也就是說該連乘積已完全加括號,則可以依此次序反覆調用 2 個矩陣相乘的標準算法計算出矩陣連乘積。完全加括號的矩陣連乘積可遞歸地定義爲:

( 1 )單個矩陣是完全加括號的;

( 2 )矩陣連乘積 A 是完全加括號的,則 A 可表示爲 2 個完全加括號的矩陣連乘積 B 和 C 的乘積並加括號,即 A= ( BC )。

 

分析:

1. 矩陣連乘的條件:第一個矩陣的列等於第二個矩陣的行,此時兩個矩陣是可乘的;

2. 多個矩陣連乘的結果矩陣,其行等於第一個矩陣的行和其列等於最後一個矩陣的列

3.兩個矩陣相乘的計算量:

例如:A(4*2),B(2*5 可知總執行次數爲:4×2×5=40

所以矩陣A m*n和B n*k的乘法運算次數爲:m*n*k;

  1. 子問題的拆分,在(AiAi+1……Aj)中,先選擇一個矩陣Ak,這樣的話就將一個大矩陣拆分爲兩個小矩陣;假設在第k位置上找到最優解,則問題變成了兩個子問題:(AiAi+1……Ak),(Ak+1……Aj)用m[i][j]表示矩陣連乘的最優值,那麼兩個子問題對應的最優值變成m[i][k],m[k+1][j]; 
  2. 矩陣連乘最優值遞歸式:
#include <stdio.h>
#define N 100// 定義最大連乘的矩陣個數爲 100 個
void matrixChain (int p[],int m[N+1][N+1],int s[N+1][N+1])
/* 用 m[i][j] 二維數組來存儲 Ai*......Aj 的最小數乘次數,用 s[i][j] 來存儲使 Ai......Aj 獲得最小數乘次數對應的斷開位置 k ,需要注意的是此處的 N+1 非常關鍵,雖然只用到的行列下標只從 1 到 N 但是下標 0 對應的元素默認也屬於該數組,所以數組的長度就應該爲 N+1*/
{
 int n=N;// 定義 m,s 數組的都是 n*n 的,不用行列下標爲 0 的元素,但包括在該數組中
 for (int i=1;i<=n;i++)
   m[i][i]=0; /* 將矩陣 m 的對角線位置上元素全部置 0 ,此時應是 r=1 的情況,表示先計算第一層對角線上個元素的值 */
 for (int r=2;r<=n;r++)//r 表示斜對角線的層數,從 2 取到 n
 {
   for (int i=1;i<=n-r+1;i++)//i 表示計算第 r 層斜對角線上第 i 行元素的值
   {
     int j=i+r-1;//j 表示當斜對角線層數爲 r ,行下標爲 i 時的列下標
     m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];// 計算當斷開位置爲 i 時對應的數乘次數
     s[i][j]=i;// 斷開位置爲 i
     for (int k=i+1;k<j;k++)
     {
       int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];/* 計算當斷開位置 k 爲從 i 到 j (不包括 i 和 j )的所有取值對應的 (Ai*......*Ak)*(Ak+1*.......Aj) 的數乘次數 */
       if (t<m[i][j])
       {
         m[i][j]=t;// 將 Ai*......Aj 的最小數乘次數存入 m[i][j]
         s[i][j]=k;// 將對應的斷開位置 k 存入 s[i][j]
       }
     }
   }
 }
}
void traceback (int i,int j,int s[][N+1])// 用遞歸來實現輸出得到最小數乘次數的表達式
{
 if (i==j)
 {
   printf ("A%d",i);
 }
 else
 {
   printf ("(");
 traceback (i,s[i][j],s);
 traceback(s[i][j]+1,j,s);
 printf (")");
 }
}
int main ()
{
 int n;// 用來存儲矩陣的個數
 int Q[2*N];/* 用 Q 數組來存儲最原始的輸入(各矩陣的行和列),主要目的是爲了檢驗這 N 個矩陣是否滿足連乘的條件 */
 int p[N+1],flag=1;/* 用 p[i-1],p[i] 數組來存儲 A 的階數, flag 用來判斷這 N 個矩陣是否滿足連乘 */
 int m[N+1][N+1];//  用 m[i][j] 二維數組來存儲 Ai*......Aj 的最小數乘次數
 int s[N+1][N+1];//  用 s[i][j] 來存儲使 Ai......Aj 獲得最小數乘次數對應的斷開位置 k
 printf (" 請輸入矩陣的個數(小於 100 ) :");
 scanf ("%d",&n);
 for (int i=0;i<=2*n-1;i++)// 各矩陣的階數的輸入先存入數組 Q 中接受檢驗
 {
   if (i%2==0)
   {
     printf ("\n");
     printf (" 請輸入A%d的 行 :",(i/2)+1);
   }
   else
   {
     printf ("             列:");
   }
 scanf ("%d",&Q[i]);
 }
 for (int i=1;i<=2*n-2;i++)// 矩陣連乘條件的檢驗
 {
 if (i%2!=0&&Q[i]!=Q[i+1])
   {
     flag=0;
     break;
   }
 }
 for (int j=1;j<=n-1;j++)
 {
     p[j]=Q[2*j];
 }
 if (flag!=0)
 {
 p[0]=Q[0];
 p[n]=Q[2*n-1];
   matrixChain (p,m,s);
   printf (" 式子如下 :\n");
   traceback(1,n,s);
   printf ("\n");
   printf (" 最小數乘次數爲 %d\n",m[1][n]);
 }
 else
 {
   printf (" 這 %d 個矩陣不能連乘 !\n",n);
 }
 }

 

  • 實驗結果與分析

(1)實驗結果

1.輸入正確結果的情況

 

  1. 輸入有誤的情況:

 

(2)分析與收穫

經過這個對這個程序的瞭解和深入研究,漸漸理解了動態規劃的基本思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章