以下內容參考(摘抄)《演算法設計與分析》,王曉東編著,清華大學出版社2023年1月第1版。
給定n個矩陣,其中ai與ai+1是可乘的,i=1,2,…,n-1。考察這n個矩陣的連乘積a1a2…an。由於矩陣乘法滿足結合律,故計算矩陣的連乘積可以有許多不同的計算次序,這種計算次序可以用加括號的方式來確定。若乙個矩陣連乘積的計算次序完全確定,則可以依此次序反覆呼叫2個矩陣相乘的標準演算法(有改進的方法,這裡不考慮)計算出矩陣連乘積。若a是乙個p×q矩陣,b是乙個q×r矩陣,則計算其乘積c=ab的標準演算法中,需要進行pqr次數乘。
矩陣連乘積的計算次序不同,計算量也不同,舉例如下:
先考察3個矩陣連乘,設這三個矩陣的維數分別為10×100,100×5,5×50。若按((a1a2)a3)方式需要的數乘次數為10×100×5+10×5×50=7500,若按(a1(a2a3))方式需要的數乘次數為100×5×50+10×100×50=75000。
下面使用動態規劃法找出矩陣連乘積的最優計算次序。
1, 設矩陣連乘積aiai+1…aj簡記為a[i:j],設最優計算次序在ak和ak+1之間斷開,則加括號方式為:
((aiai+1…ak)(ak+1…aj))
則依照這個次序,先計算a[i:k]和a[k+1:j]然後再將計算結果相乘,計算量是:
a[i:k]的計算量加上a[k+1:j]的計算量再加上它們相乘的計算量。
問題的乙個關鍵是:計算a[i:j]的最優次序所包含的兩個子過程(計算a[i:k]和a[k+1:j])也是最優次序。
2, 設計算a[i:j]所需的最少數乘次數為m[i][j]。
i=j時為單一矩陣,則m[i][i]=0,
i//p[0]:第乙個矩陣的行數
//p[1]:第乙個矩陣的列數,第二個矩陣的行數
//p[2]:第二個矩陣的列數,第三個矩陣的行數
k此時並未確定,需要從i到j-1遍歷以尋找乙個最小的m[i][j]。我們把這個最小的k放在s[i][j]。
以下是完整實現**,以乙個具體的例子實現,稍加修改即可通用。
#include
using namespace std;
//matrixchain計算m[i][j]所需的最少數乘次數
//並記錄斷開位置s[i][j]
void matrixchain(int *p,int n,int **m,int **s)
for(int i=0;im[i][i]=0;//單個矩陣相乘,所需數乘次數為0
//以下兩個迴圈是關鍵之一,以6個矩陣為例(為描述方便,m[i][j]用ij代替)
//需按照如下次序計算
//01 12 23 34 45
//02 13 24 35
//03 14 25
//04 15
//05
//下面行的計算結果將會直接用到上面的結果。例如要計算14,就會用到12,24;或者13,34等等
for(int r=1;rfor(int i=0;iint j=i+r;
//首先在i斷開,即(ai*(ai+1...aj))
m[i][j]=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1];
s[i][j]=i;
for(int k=i+1;k//然後在k(從i+1開始遍歷到j-1)斷開,即((ai...ak)*(ak+1...aj))
int t=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
if(tm[i][j]=t;//記錄最少數乘次數
s[i][j]=k;//記錄斷開位置
//如果使用下面注釋的迴圈,則是按照如下次序計算
//01 02 03 04 05
//12 13 14 15
//23 24 25
//34 35
//45
//當要計算時14,會用到12,24,而此時24並沒有被計算出來。
for(int i=0;ifor( int j=i+1;jm[i][j]=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1];
s[i][j]=i;
for(int k=i+1;kint t=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
if(tm[i][j]=t;
s[i][j]=k;
//traceback列印a[i:j]的加括號方式
void traceback(int i,int j,int **s)
//s[i][j]記錄了斷開的位置,即計算a[i:j]的加括號方式為:
//(a[i:s[i][j]])*(a[s[i][j]+1:j])
if(i==j)return;
traceback(i,s[i][j],s);//遞迴列印a[i:s[i][j]]的加括號方式
traceback(s[i][j]+1,j,s);//遞迴列印a[s[i][j]+1:j]的加括號方式
//能走到這裡說明i等於s[i][j],s[i][j]+1等於j
//也就是說這裡其實只剩下兩個矩陣,不必再分了
cout<<"a"int n=6;//矩陣的個數
int *p=new int[n+1];
//p[0]:第乙個矩陣的行數
//p[1]:第乙個矩陣的列數,第二個矩陣的行數
//p[2]:第二個矩陣的列數,第三個矩陣的行數
p[0]=30;
p[1]=35;
p[2]=15;
p[3]=5;
p[4]=10;
p[5]=20;
p[6]=25;
int **m,**s;
m=new int*[n];
for( int i=0;im[i]=new int[n];
s=new int*[n];
for(int i=0;is[i]=new int[n];
matrixchain(p,n,m,s);
traceback(0,n-1,s);
for(int i=0;idelete m[i];
m[i]=null;
delete s[i];
s[i]=null;
delete m;
m=null;
delete s;
s = null;
delete p;
p = null;
return 0;
列印結果是:
a1和a2相乘
a0和a1相乘
a3和a4相乘
a3和a5相乘
a0和a3相乘
實際上要表達的是如下加括號方式:
((a0(a1a2))((a3a4)a5))
加了括號之後用第乙個來代替,例如(a1a2)可看作a1,這個結果的數乘次數是15125。
動態規劃之矩陣連乘
假設矩陣a1 m n a2 n p 則a1 a1 m p 其中它們相乘的次數為 m n p。矩陣連乘 多個矩陣相乘,它滿足結合律,故計算矩陣連乘有許多不同的計算次序,不同的計算次序也會導致計算量 數乘次數 的不同。例如 有三個矩陣a1 10 20,a2 20 40,a3 40 30,則a1 a2 a...
動態規劃之矩陣連乘
題目描述 給定n個矩陣 a1,a2,an 其中,ai與ai 1是可乘的,i 1,2 n 1 用加括號的方法表示矩陣連乘的次序,不同的計算次序計算量 乘法次數 是不同的,找出一種加括號的方法,使得矩陣連乘的次數最小。例如 a1是a 5 10 的方陣 a2是a 10 100 的方陣 a3是a 100 2...
矩陣連乘(動態規劃)
題目描述 給定n個矩陣 a1,a2,an 其中ai與ai 1是可乘的,i 1,2 n 1。如何確定計算矩陣連乘積的計算次序,使得依此次序計算矩陣連乘積需要的數乘次數最少。例如 a1 a2 a3 a4 a5 a6 最後的結果為 a1 a2a3 a4a5 a6 最小的乘次為15125。思路 動態規劃演算...