问题描述:
给定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(" "); } } }
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(" "); } } 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