问题描述:

给定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(")");
}
}
}