動態規劃之矩陣連乘

2021-05-23 04:28:52 字數 3352 閱讀 3170

以下內容參考(摘抄)《演算法設計與分析》,王曉東編著,清華大學出版社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。思路 動態規劃演算...