动态规划-矩阵链乘法

问题描述:

  给定由n个要相乘的矩阵构成的序列(链)<A1,A2,...,An>,要计算乘积A1A2...An,可以将两个矩阵相乘的标准算法作为一个子程序,通过加括号确定计算的顺序(对同一矩阵链,不同的计算顺序所需要的计算次数大不相同)。

  目标问题:给定n个矩阵构成的矩阵链<A1,A2,...,An>,其中,i=1,2,...,n,矩阵Ai的维数为pi-1×pi,对乘积A1A2...An以一种最小计算次数加全部括号。

穷尽搜索:

  令P(n)表示一串n个矩阵可能的加全部方案数。当n=1时,只有一种方案。当n>1时,一个矩阵链乘积变成两个子矩阵链乘积的乘积,分裂的地方可能发生在第k个矩阵和第k+1个矩阵之间,k=2,3,...,n-1。那么,P(n)=∑(k=2,3,...,n-1)P(k)*P(n-k)。这是关于n的指数形式,因此穷尽搜索不是好的策略。

动态规划:

  1、寻找最优子结构

  寻找最优子结构,利用这一子结构,根据子问题的最优解构造原问题的一个最优解。对于矩阵链相乘的问题,用Ai..j表示A1A2...An的求积结果,其中i≤j。对于某个k(1<k<j),首先计算Ai..k和Ak+1..j,然后计算它们相乘最终得到Ai..j。计算Ai..j的代价就是计算Ai..k和Ak+1..j的代价之和,再加上两者相乘的代价。

  最优子结构如下:

  假设AiAi+1...Aj的最优加括号方式在k和k+1处分开,则对前缀子链AiAi+1...Ak的加括号必须是一个最优加括号,为什么呢?如果存在有对AiAi+1...Ak有一个更优的加括号,那么会产生一个对AiAi+1...Aj的更优加括号,这与假设矛盾。同理,后缀子链Ak+1Ak+2...Aj也必须是一个最优加括号。

  现在利用最优子结构,根据子问题的最优解构造原问题的最优解。即保证寻找一个最优的位置分割乘积。

  2、递归求解

  假设m[i,j]表示计算AiAi+1...Aj最优解的花费代价,1≤i≤j≤n。整个问题,计算A1..n的代价为m[1,n]。如果i=j,m[i,j]=0;当i<j时,假设最优加括号是将AiAi+1...Aj分裂成AiAi+1...Ak和Ak+1Ak+2...Aj。则m[i,j]等于计算Ai..k和Ak+1..j的最小代价,再加上这二者相乘的代价。即:

    m[i,j]=m[i,k]+m[k+1,j]+pi-1pkpj (i<k<j)

  上述公式假设已经知道k值,实际上,只知道k有j-i个值,分别是k=i,i+1,i+2...,j-1,k是其中最佳的值,需要通过如下方式确定k值:

    m[i,j]=min(i≤k<j){m[i,j]=m[i,k]+m[k+1,j]+pi-1pkpj } (i<j)

  各个m[i,j]的值给出了子问题的最优解的代价。为了跟踪如何构造一个最优解,定义s[i,j],让其记录AiAi+1...Aj最优解分裂处的k值。

  3、计算最优解的代价

    可轻易根据上述递归公式写出计算m[1,n]的递归算法。但是,它和穷尽搜索类似,具有指数时间。

    可以观察到,原问题只有较少的子问题:每一对满足i<j的(i,j)对应一个子问题,总共不超过n2个。一个递归算法在递归树的不同分支中可能会多次遇到同一个子问题,子问题重叠是判断是否能够采用动态规划方法的第二个标志(第一个标志是最优子结构)。

    对于矩阵链相乘问题不使用递归方式,而是使用自底向上的表格法计算最优解的代价。如下代码计算最优解的代价,其中Ai的维数为pi-1×pi,i=1,2,....,n。输入序列p=<p0,p1,p2,...,pn>,使用一个辅助表m[1...n,1...n]保存m[i,j]的值,使用s[1...n,1...n]记录取得最优m[i,j]时分裂处的k值。s用来构造最优解。    

 1 public void Matrix_Chain_Order()
 2     {
 3         for (int i = 1; i <= n; i++) {
 4             m[i][i] = 0;
 5         }
 6         for (int l = 2; l <= n; l++)
 7         {
 8             for (int i = 1; i <= n-l+1; i++)
 9             {
10                 int j = i+l-1;
11                 m[i][j] = 2147483647;
12                 for (int k = i; k <= j-1; k++)
13                 {
14                     int q = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
15                     if(q<m[i][j])
16                     {
17                         m[i][j] = q;
18                         s[i][j] = k;
19                     }
20                 }
21             }
22         }
23      }  

  4、构造最优解

     利用保存在表格s[1..n,1..n]内的、经过计算的信息来构造一个最优解并不难。在每一个表项s[i,j]中记录了AiAi+1..Aj在Ak与Ak+1之间取得最优解的k值。计算A1..n最优解时,最终相乘的顺序就是A1..s[1,n]As[1,n]+1..n。s[1,s[1.n]]决定A1..s[1,n]最优解处的值,s[s[1,n]+1,n]则决定As[1,n]+1..n最优解处的值。下面程序递归的计算出一个矩阵链相乘的最优顺序。  

 1 public void Print_Optimal_Parens(int i,int j)
 2     {
 3         if(i==j)
 4         {
 5             System.out.print("A"+i);
 6         }
 7         else
 8         {
 9             System.out.print("(");
10             Print_Optimal_Parens(i, s[i][j]);
11             Print_Optimal_Parens(s[i][j]+1, j);
12             System.out.print(")");
13         }
14     }

 程序实现:   

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package agrith;

/**
 *
 * @author ning
 */
public class Matrix_Chain {
    private int [] p;
    private int [][]s;
    private int [][] m;
    private int n;

    public int getN() {
        return n;
    }

    public void setN(int n) {
        this.n = n;
    }

    public int[] getP() {
        return p;
    }

    public void setP(int[] p) {
        this.p = p;
    }

    public int[][] getS() {
        return s;
    }

    public void setS(int[][] s) {
        this.s = s;
    }

    public int getM(int i,int j) {
        return m[i][j];
    }

    public void setM(int[][] m) {
        this.m = m;
    }

    public void Matrix_Chain_Order()
    {
        for (int i = 1; i <= n; i++) {
            m[i][i] = 0;
        }
        for (int l = 2; l <= n; l++)
        {
            for (int i = 1; i <= n-l+1; i++)
            {
                int j = i+l-1;
                m[i][j] = 2147483647;
                for (int k = i; k <= j-1; k++)
                {
                    int q = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                    if(q<m[i][j])
                    {
                        m[i][j] = q;
                        s[i][j] = k;
                    }
                }
            }
        }
     }  

    public void Print_Optimal_Parens(int i,int j)
    {
        if(i==j)
        {
            System.out.print("A"+i);
        }
        else
        {
            System.out.print("(");
            Print_Optimal_Parens(i, s[i][j]);
            Print_Optimal_Parens(s[i][j]+1, j);
            System.out.print(")");
        }
    }
}
        matrix_Chain.Matrix_Chain_Order();
        System.out.println("最优解花费的代价:"+matrix_Chain.getM(1, n-1));

        System.out.println("最优解的顺序:");
        matrix_Chain.Print_Optimal_Parens(1, n-1);

    }

}

程序结果:

  最优解花费的代价:15125
  最优解的顺序:
  ((A1(A2A3))((A4A5)A6))

动态规划-矩阵链乘法,布布扣,bubuko.com

时间: 2024-10-12 15:22:27

动态规划-矩阵链乘法的相关文章

[动态规划] 矩阵链乘法问题

什么是矩阵链乘法(Matrix Chain Multiplication) 矩阵链乘法问题是指给定一串矩阵序列M?M2..Mn,求至少需要进行多少次乘法运算才能求得结果 比如对于这个M?M?M?的矩阵链, 我们可以先计算M?M?然后结果乘以M?,也可以M?M?先算,然后乘以M?,为了表达方便,可以用括号表示计算顺序. 矩阵链M?M?M?有两种计算顺序:((M?M?)M?)和(M?(M?M?)). 那么不同计算顺序有什么区别? 对于((M?M?)M?): 对于(M?(M?M?)):  我们要做的就

《算法导论》读书笔记之动态规划—矩阵链乘法

前言:今天接着学习动态规划算法,学习如何用动态规划来分析解决矩阵链乘问题.首先回顾一下矩阵乘法运算法,并给出C++语言实现过程.然后采用动态规划算法分析矩阵链乘问题并给出C语言实现过程. 1.矩阵乘法 从定义可以看出:只有当矩阵A的列数与矩阵B的行数相等时A×B才有意义.一个m×r的矩阵A左乘一个r×n的矩阵B,会得到一个m×n的矩阵C.在计算机中,一个矩阵说穿了就是一个二维数组.一个m行r列的矩阵可以乘以一个r行n列的矩阵,得到的结果是一个m行n列的矩阵,其中的第i行第j列位置上的数等于前一个

第十五章 动态规划——矩阵链乘法

前言:今天接着学习动态规划算法,学习如何用动态规划来分析解决矩阵链乘问题.首先回顾一下矩阵乘法运算法,并给出C++语言实现过程.然后采用动态规划算法分析矩阵链乘问题并给出C语言实现过程. 1.矩阵乘法 从定义可以看出:只有当矩阵A的列数与矩阵B的行数相等时A×B才有意义.一个m×r的矩阵A左乘一个r×n的矩阵B,会得到一个m×n的矩阵C.在计算机中,一个矩阵说穿了就是一个二维数组.一个m行r列的矩阵可以乘以一个r行n列的矩阵,得到的结果是一个m行n列的矩阵,其中的第i行第j列位置上的数等于前一个

动态规划—矩阵链乘法

矩阵链乘问题描述 给定n个矩阵构成的一个链<A1,A2,A3,.......An>,其中i=1,2,...n,矩阵A的维数为pi-1pi,对乘积 A1A2...An 以一种最小化标量乘法次数的方式进行加全部括号. 注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序.找出这样一个结合顺序使得相乘的代价最低. 动态规划分析过程 1)最优加全部括号的结构 动态规划第一步是寻找一个最优的子结构.假设现在要计算AiAi+1....Aj的值,计算Ai...j过程当中肯

Algorithm --&gt; 矩阵链乘法

动态规划--矩阵链乘法 1.矩阵乘法 Note:只有当矩阵A的列数与矩阵B的行数相等时A×B才有意义.一个m×r的矩阵A左乘一个r×n的矩阵B,会得到一个m×n的矩阵C. #include <iostream> using namespace std; #define A_ROWS 3 #define A_COLUMNS 2 #define B_ROWS 2 #define B_COLUMNS 3 void matrix_multiply(int A[A_ROWS][A_COLUMNS],in

矩阵链乘法(动态规划)

一 题意描述: 给定由n个要相乘的矩阵构成的序列(链)<A1,A2,A3,····An>.由于矩阵满足结合律(加括号方式表示结合方式),不同的计算方式导致的求出最终计算结果的代价相异,有的花的时间很少,有的方式所花时间很多,那么下面的任务就是求出算出结果所需要的最少时间及一个最优解. 二 思路分析: 设p(n)表示一串n个矩阵可能的加全部括号方案数.当n=1时,只有一个矩阵,此时p(1)=1.当n>=2时,一个加全部括号的矩阵乘积等于两个加全部括号的子矩阵乘积的乘积,而且这两个子乘积之间

[CLRS][CH 15.2] 动态规划之矩阵链乘法

摘要 整理了矩阵链乘法的动态规划思路. 题目 给定n个要相乘的矩阵构成的序列<A1, A2, ... , An>,其中 i=1, 2, ..., n,矩阵 Ai 的维数为pi-1*pi.计算乘积 A1A2...An 的最小代价的矩阵相乘循序. 补充:矩阵乘法满足结合律,例如,乘积 A1A2A3A4 共有五种不同加括号结合形式.不同的结合形式极大的影响运算效率.当且仅当矩阵A和B相容(A.列 = B.行)时,才可以计算矩阵乘法.例如:矩阵A为p*q, 矩阵B为q*r,则相乘后得到的矩阵C为p*r

算法导论--动态规划(矩阵链乘法)

矩阵链乘法问题 给定一个n个矩阵的序列?A1,A2,A3...An?,我们要计算他们的乘积:A1A2A3...An.因为矩阵乘法满足结合律,加括号不会影响结果.可是不同的加括号方法.算法复杂度有非常大的区别: 考虑矩阵链:?A1,A2,A3?.三个矩阵规模分别为10×100.100×5.5×50 假设按((A1A2)A3)方式,须要做10?100?5=5000次,再与A3相乘,又须要10?5?50=2500,共须要7500次运算: 假设按(A1(A2A3))方式计算.共须要100?5?50+10

【算法导论】动态规划之“矩阵链乘法”问题

上一篇里,介绍了动态规划的"钢管切割"问题,这一次来看看"矩阵链乘法".所谓矩阵链乘法就是一个或一个以上的矩阵连续相乘,这里主要关注的是乘法的次数. 一.概述 以两个矩阵相乘为例,A1*A2,A1和A2为两个矩阵,假设A1的行列数是p*q,A2的行列数是q*r.注意这里由于是A1乘以A2,所以A1的列数要等于A2的行数,否则无法做矩阵乘法,满足上述条件的矩阵,我们称之为"相容"的.那么对于A1*A2而言,我们需要分别执行p*r次对应A1的行元素乘