1. 程式人生 > >dp演算法求解矩陣連乘的問題

dp演算法求解矩陣連乘的問題

一:動態規劃演算法與分治演算法的區別

動態規劃演算法與分治演算法的思想類似,都是通過將原問題分成一系列的子問題,並再求解每個子問題,將子問題合併的方式來解決問題。但是分治演算法在解決子問題有重疊的問題時,會出現很多重複的不必要的計算。

分治演算法適合解決子問題沒有重疊的情況,分治演算法將問題劃分成互不相交的子問題,遞迴的求解子問題,再將它們組合起來,求出原問題的解。

而動態規劃演算法適合於子問題重疊的情況,即不同的子問題具有公共的子子問題(子問題的求解是遞迴進行的,將其劃分為更小的子子問題)。顧在這種情況下,分治演算法會做很多不必要的計算,他會反覆的求解那些公共子問題。而動態規劃演算法對每個子問題只計算一次,將其解儲存在一個表格中,從而無需每次求解一個個子子問題的時候都重新計算,從而可以避免這些重複的工作。[1]

DP演算法的設計步奏:

1,刻畫一個最優解的結構特徵。

2,遞迴的定義最優解。

3,計算最優解的值,通常採用自底向上的方式。

4,利用計算出的資訊構造最優解。[2]

通俗的講,DP演算法的步奏就是:

1,分析問題,找出問題是否能夠用DP演算法來解決,DP問題具有的兩個特點:

a,最優子結構  如果一個問題的最優解包含子問題的最優解,我們就稱此問題具有最優解結構性質。[3]

b,重疊子問題  問題的遞迴演算法會反覆的求解相同的子問題,而不是一直生成新的子問題,DP演算法通常利用這個性質,將每個子問題求解一次,並將解存入一個      表中,當再次需要對這個子問題求解的時候,直接查表使用。[4]

2,,找出問題的遞迴求解方程

3,根據問題和遞迴方程,設計合適的資料結構來存放最有代價計算的子問題結果以及最有解的資訊。

4,根據最優解的資訊構造最有解計算方法。

二:矩陣連乘問題的DP演算法解決方法

問題描述:設有n個矩陣相乘A1*A2*A3*A4*..........*An,計算矩陣的結合方式即怎麼給矩陣加括號,來使矩陣連乘問題中所需要的計算次數最少。

故矩陣階數下標為 p0*p1  p1*p2  p2*p3 ........  pi-1*pi  ........  pn-1*pn

用m[i,j]表示Ai*Ai+1*.........*Aj矩陣連乘的標量乘法的次數,p={p0,p1,p2,p3,..........,pn}表示矩陣階數;

1,m[i,j]具有最優子結構性質,將矩陣連乘從k(i<=k<j)處斷開,將原問題分成兩個子問題m[i,k]和m[k+1,j]以及兩個子問題相乘:

m[i,j] = m[i,k] + m[k+1 , j] + pi-1 * pk * pj ; 而兩個子問題m[i,k]和m[k+1,j]又可以如此劃分為最優子結構問題。

      記當i=j時,m[i,i]=0; 即矩陣本身不需要標量乘法。

2,為簡化問題,設有A1*A2*A3*A4  共4個矩陣連乘,其相乘過程中具有有限個重疊子問題

如下圖中,m[1,4]的三種括號計算方法,每一種都會歸結到m[1,1],m[2,2],m[3,3],m[4,4]這幾個子問題的計算上來,故將該子問題計算結果存入表m中,每次計算的時候對其查表即可。


3,矩陣Ai*Ai+1*........*Aj的標量乘法次數根據斷開的位置不同,就會有不同的計算結果,應取其中標量乘法次數最少的斷開位置,並記錄該位置。

遞迴方程為:

m[i,j] = 0  i=j

m[i,j] = min(i<=k<j){m[i,k] + m[k+1 , j] + pi-1 * pk * pj} i<j

根據上訴遞迴方程,計算最優子問題重用表m和最優子問題資訊表s。可以使用二維陣列來存放這兩張表的資訊。


計算表m與表s的虛擬碼:按照演算法導論中給出

過程計算表m時,按照對角線的方向填寫表格m的資訊,實現程式碼如下:

輸入為連陳矩陣的下標階數,輸出返回最優化資訊矩陣s

	/*
	 * @param p輸入連乘矩陣的階數 P0,P1,P2,P3.........Pn
	 */
	public static int[][] optMatrix(int[] p){
		int n=p.length;
		int[][] m = new int[n][n];
		int[][] s  = new int[n][n];
		
		for(int i=1;i<n;i++){
			m[i][i] = 0;
			for(int j=i+1;j<n;j++)
				m[i][j]=Integer.MAX_VALUE;
		}
		
		for(int l=2;l<n;l++){
			for(int i=1;i<n-l+1;i++){
				int j=i+l-1;
				for(int k=i;k<j;k++){
					int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
					if(temp<m[i][j]){
						m[i][j]=temp;
						s[i][j] =k;
					}
				}
			}
		}
		return s;
	}
}

4,構造最優解,根據第三步計算的最優解資訊矩陣s,構造最優解。

得到矩陣s之後,就可以遞迴的求解子問題,演算法導論給出的虛擬碼為:

實現程式碼如下:

/*
	 * 根據最優化矩陣,構造輸出序列
	 * @param int[][] s 為最優化矩陣
	 * @param i , j 分別為輸入矩陣序列的序號,i<j,i為起始標號,j為終止標號
	 */
	public static void printOptParens(int[][] s,int i,int j){
		if(i<j){
			System.out.print("(");
			printOptParens(s, i, s[i][j]);
			printOptParens(s, s[i][j]+1, j);
			System.out.print(")");
		}
		else 
			System.out.print("A"+i);
	}
學習過程中的筆記,有些地方可能會沒那麼詳細。

[1],[2],[3],[4],出自演算法導論第三版