一、问题分析
矩阵链乘法问题
给定一个n个矩阵的序列〈A1,A2,A3...An〉〈A1,A2,A3...An〉,我们要计算他们的乘积:A1A2A3...AnA1A2A3...An,由于矩阵乘法满足结合律,加括号不会影响结果,但是不同的加括号方法,算法复杂度有很大的差别:
考虑矩阵链:〈A1,A2,A3〉:〈A1,A2,A3〉,三个矩阵规模分别为10×100、100×5、5×5010×100、100×5、5×50
如果按((A1A2)A3)((A1A2)A3)方式,需要做10∗100∗5=500010∗100∗5=5000次,再与A3A3相乘,又需要10∗5∗50=250010∗5∗50=2500,共需要7500次运算:
如果按(A1(A2A3))(A1(A2A3))方式计算,共需要100∗5∗50+10∗100∗50=75000100∗5∗50+10∗100∗50=75000次标量乘法,具有10倍的差别。可见一个好的加括号方式,对计算效率有很大影响。
为了得到所需乘法次数最少的方案,需要计算所有种方案的代价。
对一个n个矩阵的链,令P(n) 表示可供选择的括号化方案的数量。
完全括号化方案的矩阵乘积可以描述为两个完全括号化的部分相乘的形式,
P(n)=1P(n)=1 , n=1n=1
P(n)=∑n−1k=1P(k)P(n−k)P(n)=∑k=1n−1P(k)P(n−k), n≥2n≥2
k为分割点,即第k个矩阵和第k+1个矩阵之间
可以考虑动态规划来求解
二、递归式
Ai…j:表示矩阵链相乘的子问题AiAi+1…Aj;
M[i…j]:表示得到乘积Ai…j所用的最少基本运算次数;
假设,最后一次相乘发生在矩阵链Ai…k和Ak+1…j之间,即AiAi+1…Aj=(AiAi+1)(Ak+1Ak+2…Aj)k=i,i+1,…,j-1
public class MatrixChain {
private int array[][];
private int p[];
private int s[][];
public MatrixChain(){
p=new int[]{3,5,2,1,10};
array=new int[4][4];
s=new int[4][4];
p=new int[]{2,7,3,6,10};
array=new int[4][4];
s=new int[4][4];
p=new int[]{10,3,15,12,7,2};
array=new int[5][5];
s=new int[5][5];
p=new int[]{7,2,4,15,20,5};
array=new int[5][5];
s=new int[5][5];
}
/**动态规划*/
public void martixChain(){
int n=array.length;
for(int i=0;i<n;i++){
array[i][i]=0;//初始化0
}
for(int r=2;r<=n;r++){ //不同规模的子问题两个矩阵相乘
for(int i=0;i<=n-r;i++){ //每一个规模为r的矩阵连乘序列的首矩阵Ai
int j=i+r-1; //每一个规模为r的矩阵连乘序列的尾矩阵Aj
// 决策为k=i的乘法次数
array[i][j]=array[i+1][j]+p[i]*p[i+1]*p[j+1];
s[i][j]=i;
for(int k=i+1;k<j;k++){ //对Ai...Aj的所有决策,求最优值,记录最优决策
int t=array[i][k]+array[k+1][j]+p[i]*p[k+1]*p[j];
if(t<array[i][j]){
array[i][j]=t;
s[i][j]=k;
}
}
}
}
}
/*
* 待求矩阵为:Ai...Aj
*/
public void traceBack(int i ,int j){
if(i<j){
traceBack(i,s[i][j]);
traceBack(s[i][j]+1,j);
if(i==s[i][j] && (s[i][j]+1)==j){
System.out.println("把A"+(i+1)+"到A"+(j+1)+"括起来");
}else if(i==s[i][j] && (s[i][j]+1)!=j){
System.out.println("把A"+((s[i][j]+1)+1)+"到A"+(j+1)+"括起来,在把A"+(i+1)+"到A"+(j+1)+"括起来");
}else if(i!=s[i][j] && (s[i][j]+1)==j){
System.out.println("把A"+(i+1)+"到A"+(s[i][j]+1)+"括起来,在把A"+(i+1)+"到A"+(j+1)+"括起来");
}else{
System.out.println("把A"+(i+1)+"到A"+(s[i][j]+1)+"括起来,在把A"+((s[i][j]+1)+1)+"到A"+(j+1)+"括起来,然后把A"+(i+1)+"到A"+(j+1)+"括起来");
}
}
}
public static void main(String[] args) {
MatrixChain m=new MatrixChain();
m.martixChain();
System.out.println("结果1:");
m.traceBack(0, 3);
System.out.println("结果2:");
m.traceBack(0, 3);
System.out.println("结果3:");
m.traceBack(0, 4);
System.out.println("结果4:");
m.traceBack(0, 4);
}
}
//嵌套循环需要O(n)3