【算法】算法-矩阵连乘问题(枚举法、备忘录法、动态规划)

1,问题

给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。

2,枚举法(穷举搜索法)

枚举所有加括号的方式。
对于n个矩阵的连乘积,设其不同的计算次序为P(n)。每个问题可分解为两个子问题:(A1…Ak)(Ak+1…An)可以得到关于P(n)的递推式如下:
枚举法复杂度分析
以上递推关系说明,P(n)是随n的增长呈指数增长的。因此,穷举法不是一个多项式时间复杂度算法。

3,备忘录算法

1)概念

备忘录方法是动态规划算法的变形。用表格保存子问题答案,避免重复计算。
与动态规划不同的是:备忘录方法的递归是自顶向下的,而动态规划是自底向上的。
备忘录方法为每个问题建立一个记录项(如赋初值为0),初始化时,该记录项存入一个特殊值,表示该问题尚未求解。

2)分析

备忘录算法示意图
上图为枚举过程,其中黑色方格为重复计算。备忘录就是用来保存计算结果,在每次计算前查表,如果计算过,则直接取值,避免重复计算。

4,动态递归算法

以矩阵链ABCD为例:
矩阵链长度为n。
n=1时,分别计算A、B、C、D最优值。
n=2时,分别计算AB、BC、CD最优值。
n=3时,分别计算ABC、BCD最优值。
n=4时,分别计算ABCD的最优值。

令:
k:为矩阵链断开的位置
d:数组存放矩阵链计算的最优值,d[i][j]是以第i个矩阵为首,第j个矩阵为尾的矩阵链的最优值,i > 0
m:存放矩阵链行列信息的数组,m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3…)
方程

5,代码

package MatrixMultiply;

public class Main {

	public static void main(String[] args) {

		/**
		 * 6个矩阵连乘
		 * A1:30*35
		 * A2:35*15
		 * A3:15*5
		 * A4:5*10
		 */
		final int L = 7;
	    int[] p={30,35,15,5,10};  
	    int N = MatrixMultiply.Best_Enum(p, 1, 4);
	    System.out.println("Best_Enum矩阵计算最少次数:" + N);
	    N = MatrixMultiply.Best_Memo(p, 1, 4);
	    System.out.println("Best_Memo矩阵计算最少次数:" + N);
	    N = MatrixMultiply.Best_DP(p, p.length);
	    System.out.println("Best_DP矩阵计算最少次数:" + N);
	}
}

package MatrixMultiply;

public class MatrixMultiply {

	private final static int MAX_INT = 99999999;// 表示无穷大
	private final static int SIZE = 10;// 数组大小

	/**
	 * 枚举法(递归算法)
	 * 
	 * @param m
	 *            矩阵 m数组内存放矩阵链的行列信息 m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3...)
	 * @param left
	 *            左括号索引
	 * @param right
	 *            右括号索引
	 * @return
	 */
	public static int Best_Enum(int m[], int left, int right) {
		// 只有一个矩阵时,返回计算次数0
		if (left == right) {
			return 0;
		}

		int min = MAX_INT; // 无穷大
		int i;
		// 括号依次加在第1、2、3...n-1个矩阵后面
		for (i = left; i < right; i++) {
			// 计算出这种完全加括号方式的计算次数
			int count = Best_Enum(m, left, i) + Best_Enum(m, i + 1, right);
			count += m[left - 1] * m[i] * m[right];
			// 选出最小的
			if (count < min) {
				min = count;
			}
		}
		return min;
	}

	public static int[][] memo = new int[SIZE][SIZE];

	/**
	 * 备忘录法
	 * 
	 * @param m
	 *            m数组内存放矩阵链的行列信息 m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3...)
	 * @param left
	 * @param right
	 * @return
	 */
	public static int Best_Memo(int m[], int left, int right) {
		// 只有一个矩阵时,返回计算次数0
		if (left == right) {
			return 0;
		}

		int min = MAX_INT;
		int i;
		// 括号依次加在第1、2、3...n-1个矩阵后面
		for (i = left; i < right; i++) {
			// 计算出这种完全加括号方式的计算次数
			int count;
			if (memo[left][i] == 0) {
				memo[left][i] = Best_Memo(m, left, i);
			}
			count = memo[left][i];
			if (memo[i + 1][right] == 0) {
				memo[i + 1][right] = Best_Memo(m, i + 1, right);
			}
			count += memo[i + 1][right];
			count += m[left - 1] * m[i] * m[right];
			// 选出最小的
			if (count < min) {
				min = count;
			}
		}
		return min;
	}

	private static int[][] d = new int[SIZE][SIZE]; // 存放矩阵链计算的最优值,d[i][j]为第i个矩阵到第j个矩阵的矩阵链的最优值,i
													// > 0

	/**
	 * 动态规划算法
	 * 
	 * @param n
	 * @return
	 */
	public static int Best_DP(int[] m, int n) {
		// 把d[i][i]置为0,1 <= i < n
		for (int i = 0; i < SIZE; i++) {
			java.util.Arrays.fill(d[i], 0);
		}

		int len;
		// 递归计算矩阵链的连乘最优值
		// len = 1,代表矩阵链由两个矩阵构成
		for (len = 1; len < n; len++) {
			int i, j, k;
			for (i = 1, j = i + len; j < n; i++, j++) {
				int min = MAX_INT; // 无穷大
				for (k = i; k < j; k++) {
					int count = d[i][k] + d[k + 1][j] + m[i - 1] * m[k] * m[j];
					if (count < min) {
						min = count;
					}
				}
				d[i][j] = min;
			}
		}
		return d[1][n - 1];
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值