矩阵链乘法

矩阵链乘法要用到动态规划的思想,和分治相类似,都是通过组合子问题的解来求解原问题,但是与分治不同的是,分治要求子问题与原问题只有规模大小的区别,而动态规划要求子问题拥有重叠的子子问题。这样通过动态规划自底向上的求解,就能求出每个子问题的最优解,避免了重复计算。

例:

求该矩阵的最小运算量,我们不关心矩阵中的元素到底是什么,我们只关心要进行多少次运算。

例如A1和A2相乘需要30*35*15次运算。

定义从第i个矩阵到第j个矩阵的运算量为m[i,j]

则可以得到

当i=j时,自然是0,因为只有一个矩阵不需要进行运算。

当i≤j时,我们让k从i+1到j-1遍历,比较找到最小的那个以确定分隔的位置。

在计算好之后,我们将这个数值记录,这样在计算下一个规模更大的子问题时,就不用再重复计算了。

例如我们通过计算得到A1,A2,A3需要先算A2*A3,再乘A1,那么在计算A1,A2,A3,A4的时候,就可以直接拿来用。

建立一个m[i,j]用于记录运算量,建立一个s[i,j]用于记录分隔点

举例说明一下这两个表是怎么算的,例如m[1,3]和s[1,3],首先我们可以看到A1*A2*A3只有两种情况,即(A1(A2A3))和((A1A2)A3),第一种情况为2625+30*35*5=7875,第二种情况为15750+30*15*5=18000,显然第一种情况更小,所以应在A1,A2之间分隔,即m[1,3]=7875,s[1,3]=1

剩下的情况类似,直到最后算得m[1,6]即为该矩阵链乘法的最小运算量。

代码如下:

public class Test0202 {

	private static int[][] m;//用于记录运算量
	private static int[][] s;//用于记录分隔点
	private static int[] p;//用于记录矩阵链
	
	public static void main(String[] args) {

		init ();
		matrixChain ();
	}
	
	public static void init () {
		m = new int[6][6];
		s = new int[6][6];
		p = new int[7];
		for (int i = 0; i < p.length; i++) {
			p[i] = random ();
		}
		for (int i = 0; i < p.length; i++) {
			System.out.print (p[i] + "  ");
		}
		System.out.println ();
	}
	
	public static int random () {
		return 1 + (int)(Math.random () * 100);
	}
	
	public static void matrixChain () {
		
		int n = m.length;
		
		//对角线上的值为0,矩阵不会和自己相乘
		for (int i = 0; i < n; i++) {
			m[i][i] = 0;
		}
		
		//矩阵规模,从2个到n个
		for (int L = 2; L <= n; L++) {
			//表示一共有多少种取法,比如6个矩阵取2个就有5种取法
			//i记为已取矩阵的头矩阵
			for (int i = 0; i < n - L + 1; i++) {
				//j记为已取矩阵的尾矩阵
				int j = i + L - 1;
				//初始化m[i][j]的值为无限大
				m[i][j] = Integer.MAX_VALUE;
				for (int k = i; k <= j - 1; k++) {
					int q = m[i][k] + m[k + 1][j] + p[i]*p[k + 1]*p[j + 1];
					if (q < m[i][j]) {
						m[i][j] = q;
						s[i][j] = k;
					}
				}
			}
		}
		
		System.out.println ("该矩阵链的最小计算量是:" + m[0][n - 1]);
		System.out.println ("计算量记录表");
		for(int i = 0; i < m.length; i++){
            for(int j = 0; j < m[i].length; j++){
                System.out.printf("%8d", m[i][j]);
            }
            System.out.println();
        }
		
		System.out.println ("分隔点记录表");
		for(int i = 0; i < s.length; i++){
            for(int j = 0; j < s[i].length; j++){
            	if (i >= j) {
            		System.out.printf ("%8d", 0);
            	} else {
            		System.out.printf ("%8d", s[i][j] + 1);
     
            	}
            }
            System.out.println();
        }
		
		print (s, 0, n - 1);
	}
	
	public static void print (int[][] s, int i, int j) {
		if (i == j) {
			System.out.print ("A" + (i + 1));
		} else {
			System.out.print ("(");
			print (s, i, s[i][j]);
			print (s, s[i][j] + 1, j);
			System.out.print (")");
		}
	}
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值