矩阵链的乘法问题

设A1,A2,...,An为n个矩阵的序列,其中Ai为pi-1Xpi阶矩阵,i=1,2,...,n.这个矩阵的输入用向量P=<p0,p1,..,pn>给出,其中p0是矩阵A1的行数,pi(i=1,2,...,n-1)既是矩阵Ai的列数也是矩阵Ai+1的行数,最后的pn是矩阵An的列数;

例:P=<30,35,15,5,10,20>

则对应的矩阵序列:A1:30X35 A2:35X15 A3:15X5 A4:5X10 A5:10X20

对于该矩阵序列A1XA2XA3XA4XA5的乘法满足结合率,所以无论采用哪种顺序结果都是一样的,但是计算的工作量是不一样的.

比如A1:10X100 A2:100X5 A3:5X50

以元素相乘作为基本运算:

(A1A2)A3的工作量是10X100X5+10X5X50=7500

A1(A2A3)的工作量是10X100X50+100X5X50=75000

工作量相差10倍.

现在我们要找出一种乘法的顺序使得计算量最少.

1.蛮干法时间复杂度O(2^2n/n^(3/2)) 指数的时间

2.动态规划的时间复杂度O(n^3) 多项式的时间


下面就是使用动态规划的方法来解决:

package com.动态规划;

public class MatrixChain {

	//记录A[i][j]所需最小运算次数
	private int min[][]  ;
	//最后一次运算的位置
	private int s[][];
	//存放矩阵的输入向量
	private int p[];
	//存放矩阵的个数
	private int num;
	
	public MatrixChain(int[] p){
		if(null==p||p.length==1){
			return ;
		}
		//矩阵的个数为如数向量的个数减1
		int num = p.length-1;
		//初始化min[][],s[][]
		min = new int[num+1][num+1];
		s = new int[num+1][num+1];
		for(int i=1;i<=num;i++){
			for(int j=1;j<=num;j++){
				min[i][j] = 0;
				s[i][j] = 0;
			}
		}
		this.num = num;
		this.p = p;
		//进行计算
		matrixchain();
	}
	//关键代码
	private void matrixchain(){
		//r代表当前计算的链长(子问题规模)
		for(int r=2;r<=num;r++){
			//num-r+1表示最后一个r长的链的前边界
			for(int i=1;i<=num-r+1;i++){
				int j = i+r-1;//计算前边界为i长度为r的链的后边界
				min[i][j] = min[i+1][j]+p[i-1]*p[i]*p[j];//划分为Ai(Ai+1...Aj)
				s[i][j] = i;
				//更新最小的子问题的乘积次数
				for(int k=i+1;k<=j-1;k++){//i<k<j
					int t = min[i][k]+min[k+1][j]+p[i-1]*p[k]*p[j];//划分为(Ai...Ak)(Ak+1...Aj)
					if(t<min[i][j]){//若该种划分比原来的划分所取得的值小则更新
						min[i][j] = t;//更新最小值
						s[i][j] = k;//更新备忘录
					}
				}
			}
		}
	}
	
	public void printMatrix(){
		System.out.println("-----------------------------");
		print(this.min);
		System.out.println("-----------------------------");
	}
	
	public void printTrace(){
		System.out.println("-----------------------------");
		print(this.s);
		System.out.println("-----------------------------");
	}
	
	private void print(int list[][]){
		for(int i=1;i<list.length;i++){
			for(int j=1;j<list.length;j++){
				System.out.print("["+i+"]"+"["+j+"]:"+list[i][j]+" ");
			}
			System.out.println();
		}
	}
	
	public static void main(String[] args) {
		int[] p = {30,35,15,5,10,20};
		MatrixChain mc = new MatrixChain(p);
		mc.printMatrix();
		mc.printTrace();
	}
}
结果:

-----------------------------
[1][1]:0 [1][2]:15750 [1][3]:7875 [1][4]:9375 [1][5]:11875 
[2][1]:0 [2][2]:0 [2][3]:2625 [2][4]:4375 [2][5]:7125 
[3][1]:0 [3][2]:0 [3][3]:0 [3][4]:750 [3][5]:2500 
[4][1]:0 [4][2]:0 [4][3]:0 [4][4]:0 [4][5]:1000 
[5][1]:0 [5][2]:0 [5][3]:0 [5][4]:0 [5][5]:0 
-----------------------------
-----------------------------
[1][1]:0 [1][2]:1 [1][3]:1 [1][4]:3 [1][5]:3 
[2][1]:0 [2][2]:0 [2][3]:2 [2][4]:3 [2][5]:3 
[3][1]:0 [3][2]:0 [3][3]:0 [3][4]:3 [3][5]:3 
[4][1]:0 [4][2]:0 [4][3]:0 [4][4]:0 [4][5]:4 
[5][1]:0 [5][2]:0 [5][3]:0 [5][4]:0 [5][5]:0 
-----------------------------
上面一个矩阵是min

下面一个矩阵是s

可以看到[1][5]:11875 就是该矩阵链乘积工作量的最小值

通过min,s来分析划分的次序:

首先看到min[1][5]表示该矩阵链A1...A5的最小的工作量,对应的s[1][5]=3,表示该划分在A3的后面,即是(A1A2A3)(A4A5),再看s[1][3]=1,即是A1(A2A3)(A4A5)

更详细的讲解请参考算法设计与分析第3章但是上面有一点小错误我在代码中已经修改,请对照看.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值