動態規劃 矩陣連乘問題

2021-06-08 06:43:06 字數 4249 閱讀 1274

給定n+1個矩陣[a0, a1, a2, ……, an-1],其中ai與ai+1是可乘的,i=0,1,2,...,n-2。

矩陣乘法滿足結合律。考察這n個矩陣的連乘積,得出運算次數最少的結合。

首先,考慮兩個矩陣相乘。

如果a、b兩個矩陣可以相乘,那麼a、b的形式必定滿足:a[p][q]、b[q][r],

設c=a*b,那麼c滿足c[p][r]

c[i][j]=a[i][0]*b[0][j] + a[i][1]*b[1][j] + ... + a[i][k]*b[k][j] + ... + a[i][q-1]*b[q-1][j],其中k=0,1,2,...,q-1

一橫 一豎 對應相乘再相加,共q次乘法,q次加法

c=c[0][0],...c[i][j],...c[p-1][r-1],其中,i=0,1,2,...,p-1;j=0,1,2,...,r-1

所以,c=a*b 共需p*q*r次乘法。

例子:三個矩陣a1,a2,a3,分別為10*100,100*5,5*50

按照(a1*a2)*a3,需要 10*100*5+10*5*50=7500 次乘法

按照a1*(a2*a3),需要 10*100*50+100*5*50=75000 次乘法

由此,可以看出,矩陣連乘時,如何結合,對最終的運算量影響極大。因此,需要在開始運算連乘積之前,得出如何使用結合律使得運算量最小。

(1)最優子結構

整個問題的最優解,一定由一些子問題的最優解組成

連乘矩陣的維度必然符合:

p0*p1 p1*p2 p2*p3 ... pi*pi+1 ... pn-1*pn

a0     a1   a2   ...  ai     ...  an-1

將ai*...*aj記作a[i][j],那麼整個問題即為a[0][n-1]

假設a[0][n-1]的最優解為 a[0][k] * a[k+1][n-1],那麼a[0][k]和a[k+1][n-1]也要分別取到對應的最優解,如果不是,那麼就矛盾了。

整個問題的最優解包含了子問題的最優解,這種性質稱為 最優子結構性質

(2) 建立遞迴關係

自頂向下,建立遞迴關係,至最原子態的問題

由(1)可得到求出a[0][n-1]的最優解,即是整個問題的最優解。

設定min[i][j]為a[i][j]的最優解,0<=i<=j<=n-1

如果i==j,a[i][j]為ai自身,無計算,故min[i][i]=0,i=0,1,2,...,n-1

如果i!=j,則取i~j之間的每種結合的最小值。假設a[i][j]的最優解在a[k]和a[k+1]之間斷開,那麼min[i][j] = min[i][k] + min[k+1][j] + pi*pk+1*pj+1。

所以min[i][j] =min(min[i][k] + min[k+1][j] + pi*pk+1*pj+1)k=i,i+1,...,j

(3) 自底向上,計算各個子問題的最優解

由最原子態的問題的最優解開始,構建全部子問題的最優解,並記錄過程

由(2)可看出,很容易即可寫出乙個遞迴演算法來算出min[0][n-1]

/**

* matrix chain multiply

* a0 * a1 * a2 * ... * an-1

*/public class mcm

/*** a[i]的維度為p[i]*p[i+1]

* 遞迴解法,求解m[i][j]

*/public static int mcm1(int p, int i, int j)

return min;

} public static void main(string args) ; // 30*35, 35*15, 15*5, 5*10, 10*20, 20*25

int min = mcm1(p);

system.out.println(min);

}}

可以看出,重複運算了好多問題:

mcm1(p, i, k) + mcm1(p, k + 1, j) + p[i] * p[k + 1] * p[j + 1];
據說,遞迴演算法需要指數級運算時間。

很多的重複子問題,這也是動態規劃的特點之一。

因此可以採取自底向上的運算方法,由a[0][0]~a[i][j]~a[0][n],運算過程中,將每個子問題的解儲存起來,即min[i][j],可避免大量的重複計算

/**

* a[i]的維度為p[i]*p[i+1]

* 動態規劃解法

* a0,a1,a2,a3,a4,a5

* n=6

* 計算順序:

* 00 11 22 33 44 55

* 01 12 23 34 45 r=1,i=0~4,j=1~5, j=i+r

* 02 13 24 35 r=2,i=0~3,j=2~5, j=i+r

* 03 14 25 r=3,i=0~2,j=3~5, j=i+r

* 04 15 r=4,i=0~1,j=4~5, j=i+r

* 05 r=5,i=0~0,j=5~5, j=i+r

*/public static int mcm2(int p)

}min[i][j] = m;

system.out.println(i + "," + j + " : " + m);

} }// 求解整個問題的最優解

traceback(pos);

return min[0][n - 1];

}

(4)求解整個問題的最優解

由全部子問題的最優解,自頂向下,組合出整個問題的最優解

在(3)中,pos記錄了min[i][j]是在**斷開的

public static void traceback(int pos) 

public static void traceback(int pos, int i, int j) else

}

可以得出結果:

((a0(a1a2))((a3a4)a5))

2012-08-04 12:44 修訂

上面(3)中的遞迴演算法中,計算m[i][j]的時候,做了很多的重複運算。

備忘錄方法

動態規劃演算法的變形。也將已經計算過的子問題儲存下來,供下次再計算此子問題時,可以直接檢視,無需再次運算,是為備忘錄方法。

區別在於:

(1)動態規劃是自底向上的,從最原子態的子問題算起,一直遞迴到整個問題;而備忘錄方法是自頂向下的,與普通的遞迴方式相同。

(2)動態規劃是所有的子問題都會運算一遍,不管最後用到或是用不到;而備忘錄方法,自頂向下決定了它只會運算所需要的子問題。

因此,當所有的子問題都需要運算時,動態規劃演算法較優,畢竟遞迴運算有些效率問題;當不是所有的子問題都需要運算時,普通的遞迴方式+備忘錄方法 較優。

上文中的mcm1使用了備忘錄後的**如下所示,由o(2^n)減到了o(n^3)

/**

* a[i]的維度為p[i]*p[i+1]

* 遞迴解法,備忘錄方法

* 遞迴的過程中,記錄下已經計算出來的子問題,下次需要訪問此子問題時,不需再重複計算

*/public static int mcm3(int p)

} int min = mcm3(p, 0, n - 1, mins, pos);

traceback(pos);

return min;

}/**

* a[i]的維度為p[i]*p[i+1]

* 遞迴解法,求解m[i][j]

*/public static int mcm3(int p, int i, int j, int mins, int pos)

} mins[i][j] = min;

pos[i][j] = tmp2;

return min;

}

輸出結果為:

((a0(a1a2))((a3a4)a5))

15125

矩陣連乘 動態規劃 動態規劃解矩陣連乘問題

一.矩陣鏈事例 矩陣鏈問題主要涉及的時在多個矩陣相乘,如何通過相乘的順序來減少程式執行。二.例題分析 這次分析過程按照動態規劃的三個基本條件來逐步解答 1 尋找最優子結構 假設我們已經找到父矩陣鏈最優解,當我們劃分到最後一步時都是兩個子矩陣鏈 分別被括號包圍 相乘,如 a1a2a3a4 a5a6a7...

python矩陣連乘 動態規劃 矩陣連乘問題

一 問題描述 給定n個數字矩陣a1,a2,an,其中ai與ai 1是可乘的,設ai是pi 1 pi矩陣,i 1,2,n。求矩陣連乘a1a2.an的加括號方法,使得所用的乘次數最少。例子三個矩陣連乘,可以有 a1a2 a3和a1 a2a3 兩種方法求積 乘法次數分別為 p0p1p2 p0p2p3和p0...

動態規劃 矩陣連乘問題

以下只是對此問題的乙個 實現,具體理論部分請參見王曉東 演算法設計與分析 第2 版3.1 節 矩陣連乘問題。include include using namespace std define max count 20 矩陣屬性 struct tagmatrixattribute 矩陣連乘加括號求解...