动态规划(三)--矩阵链乘法

问题描述:

给定n个矩阵的链<A1,A2,...,An>,矩阵Ai的规模为Pi-1XPi。求完全括号方案使得计算乘积所需的标量乘法次数最少。

为了计算上式,我们可以先用括号明确计算次序,然后利用标准矩阵相乘方法进行计算。例如矩阵链<A1,A2,A3,A4,A5>,由于矩阵乘法满足结合律,所以可以有((A1A2)(A3A4)A5)或(A1(A2(A3A4))A5)等计算次序。而对于相容的矩阵A,B,若A矩阵为p*q,B矩阵为q*r,那么乘积C是p*r的矩阵,而计算时间是标量乘法的次数决定的即p*q*r。假设有三个矩阵的规模为10*100,100*5,5*50,如果按照((A1A2)A3)的顺序计算则需要7500次标量乘法,若按(A1(A2A3))次序计算,需要75000次乘法,时间相差了十倍。。要明确的是我们的目标是确定代价最低的计算顺序,并不是真正进行矩阵相乘。

令P(n)表示可供选择的括号化方案的数量,则p(n)=1(n=1);

     p(n)=∑p(k)p(n-k) (n>=2);

对应第一篇的原理,

Step1:我们要寻找最优子结构,对应Ai~Aj(i<j),为了对Ai...Aj进行括号化,需要在某个Ak和Ak+1之间将矩阵链分开,然后在计算乘积得到结果Aij,即原问题可以分解成为两个互不相干的子问题并分别进行独立求解

Step2:一个递归的求解方案

显然最低代价m[i,j]=0(i=j)   ;   m[i,j]=min{m[i,k]+m[k+1,j]}+Pi-1PkPj(i<j)

step3:计算最优代价

采用子底向上的方法,用一个一维数组p[n+1]来表示矩阵链,用一个二维数组m[i][j]来保存代价,用另一个辅助二维数组s[i][j]记录最优值对应的分割点k,我们就可以用s来构造最优解了

pseudoCode

MATRIX-CHAIN-ORDER(p)

n = p.length -1

  let m and s be new tables

for i = 1  to n

m[i][i] = 0

for l = 2 to n

for i =1 to n-l+1

j=i+l-1

m[i,j] = max

for k = i to j-1

q=m[i,k]+m[k+1,j]}+Pi-1PkPj

 ifq<m[i,j]

m[i,j] = q

s[i,j] = k

return m and s


java代码

 

import java.util.Arrays;


/**
 * @author Bangwen Chen
 *
 * 2013-8-22
 */
public class Matrix_chain_order {
	public static void main(String [] args){
		int[]p = {30,35,15,5,10,20,25};
		chain_order(p);
	}
	static void chain_order(int []p){
		final double MAX=1E10f;
		int n = p.length-1;
		 double [][]m = new double[n+1][n+1];
		double [][]s = new double [n+1][n+1];
		for(int i=1;i<n+1;i++){
			m[i][i]=0;
		}
		for(int l=2;l<n+1;l++){
			for(int i=1;i<n-l+2;i++){
				int j=i+l-1;
				m[i][j]=MAX;
				for(int k=i;k<j-1+1;k++){
					double q = (double)m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
					System.out.println("q" + q);
					if(q<m[i][j]){
						m[i][j] = q;
						s[i][j] = k;
					}
				}
			}
		}
		display(m);
		System.out.println("-------------------");
		display(s);
	}
	static void display(double [][] array){
		int n = array[0].length;
		for(int i=1;i<n;i++){
			for(int j=1;j<n;j++){
				System.out.print(array[i][j]+" ");
			}
			System.out.print("\n");
		}
	}
}


step 4 构造最优解,Matrix-chain-order求出了最少的标量乘法,但它并未指出如何进行这种最优代价的矩阵链乘法计算,s中保存了最优解,利用递归给出。

 

pseudocode

PRINT-OPTIMAL-PARENS(s,i,j)

if i == j

print Ai;

else

print "("

PRINT-OPTIMAL-PARENS(s,i,s[i,j])

PRINT-OPTIMAL-PARENS(s,s[i,j+1,j)

print")"

Java代码

 

import java.util.Arrays;


/**
 * @author Bangwen Chen
 *
 * 2013-8-22
 */
public class Matrix_chain_order_display {
	public static void main(String [] args){
		int[]p = {30,35,15,5,10,20,25};
		int [] array = {5,10,3,12,5,50,6};
		chain_order(array,1,6);
	}
	static void chain_order(int []p,int start,int end){
		final double MAX=1E10f;
		int n = p.length-1;
		 double [][]m = new double[n+1][n+1];
		double [][]s = new double [n+1][n+1];
		for(int i=1;i<n+1;i++){
			m[i][i]=0;
		}
		for(int l=2;l<n+1;l++){
			for(int i=1;i<n-l+2;i++){
				int j=i+l-1;
				m[i][j]=MAX;
				for(int k=i;k<j-1+1;k++){
					double q = (double)m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
					System.out.println("q" + q);
					if(q<m[i][j]){
						m[i][j] = q;
						s[i][j] = k;
					}
				}
			}
		}
		display(m);
		System.out.println("-------------------");
		display(s);
		String A[]=new String [n+1];
		for(int z=1;z<n+1;z++){
			A[z] = "A".concat(String.valueOf(z));
		}
		print_optimal_parens(s,start,end,A);
	}
	
	static void display(double [][] array){
		int n = array[0].length;
		for(int i=1;i<n;i++){
			for(int j=1;j<n;j++){
				System.out.print(array[i][j]+" ");
			}
			System.out.print("\n");
		}
	}
	static void print_optimal_parens(double[][] s,int i,int j,String A[]){
		
		if(i == j){
			System.out.print(A[i]);
		}else{
			System.out.print("(");
			int tmp = (int) s[i][j];
			print_optimal_parens(s,i,tmp,A);
			print_optimal_parens(s,tmp+1,j,A);
			System.out.print(")");
		}
	}
}


Sep 2

 

 

posted on 2013-09-02 19:13  you Richer  阅读(366)  评论(0编辑  收藏  举报