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

来源:互联网 发布:卖淘宝号的网站 编辑:程序博客网 时间:2024/06/04 18:49

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];    }}
阅读全文
0 0