动态规划---矩阵连乘

引言:马上期末考试了,最近在复习计算机算法分析与程序设计;动态规划,这门课程中最难的几个部分之一,上课老师讲时自己懵懵懂懂的以为自己懂了,今天下午复习时。蒙圈了!!!。研究一个晚上,算是稍微开了点窍,遂做如下笔记:
一:问题提出:
给定n个矩阵{A1…..An};其中A1….Ai+1是可以连乘的,先要求怎么样给这些矩阵加括号改变他们原来的乘积顺序使得最终相乘的次数达到最小:
二:求解
第一种方法:穷举法:
列出所有的可能结果然后一个一个对比:对于n个矩阵设有不同的计算次数P(n)。由于可以先在第k个和第k+1个矩阵之间将原矩阵分为两个矩阵子序列,k=1,2,3,4……n-1;然后然后分别对这两个矩阵完全加括号,最后得到的结果再加括号,得到原矩阵的一种完全加括号的方式。由此我们可以得到关于P(n)的如下递归式:

P(n).png

化简得到P(n)随着n呈现指数增长,所以穷举法不是一个有效的算法,遂提出使用动态规划的思想求解此题:
第二种方法:动态规划:
*第一步:分析其最优子结构:****
设矩阵Ai
A2…..Aj,就是我们所要求的目标;计做A[i,j];考察计算A[1:n]的最优计算次序;我们将矩阵在Ak和Ak+1出进行分割得到A1…Ak和Ak+1….An的连乘;其中1<=k<n;其相应的完全加括号方式为((A1….Ak)(Ak+1….An));;我们先计算A[1:k]和A[k+1:n]然后再将计算结果相乘便可以得到A[1:n];所以总的计算量就是A[1:k]计算量+A[k+1:n]计算量+A[1:k]和A[k+1:n]相乘的计算量。

关键:计算A[1:n的最有次序所包含的计算矩阵子链A[1:k]和A[k+1:n]的次序也是最优的。(当然是最优的)证明如下:
若有一个计算A[1:k]的次序需要的计算量更少,则用此次序替换原来的A[1:k]的次序,得到的计算A[1:n]的计算量将比最优次序所需要计算量更少,自相矛盾。同理可知,计算A[1:n]的最有次序所包含的计算矩阵子链A[k+1:n]的次序也是最优的。
所以:矩阵连乘积计算次序问题的最优解包含着其子问题的最优解。满足最有子结构性质。

2:建立递归关系:
计算A[i:j]1<=i<=j<=n;所需要的最少乘次数为m[i][j],则原问题的最优值为
M[1][n]。
当i=j时,A[i:j]=Ai,为单一矩阵不需要计算:所以m[i][i] =0;i=1,2,3…..n。
当i<j时,可以利用最优子结构性质来计算m[i][j]。事实上,若计算A[i:j]最有次序在Ak和Ak+1之间断开时i<=k<j;m[i][j] = m[i][k]+m[k+1][j]+P(i-1)PkPj;由于在计算时我们并不知道k的具体位置k可以去j-i种可能。所以k是这j-i种可能中使得计算量达到最小的那个位置,从而m[i][j]可以递归定义为:

1.png

3:计算最优值
对于1≤i≤j≤n不同的有序对(i,j) 对于不同的子问题,因此不同子问题的个数最多只有o(n*n).但是若采用递归求解的话,许多子问题将被重复求解,所以子问题被重复求解,这也是适合用动态规划法解题的主要特征之一。

用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法。
具体代码如下:

package Ceshi;

public class Strassen {
    //Arrays[i][j]表示Ai....Aj连乘最少计算次数;
    private int[][] Arrays;
    //s[i][j] = k;表示矩阵分为Ai...Aj的最优子结构为Ai...Ak和Ak+1...Aj
    private int[][] s;
    //p[i]表示Ai的行数,p[i+1]表示Ai的列数
    private int[] p;
    //提供的默认的构造方法
    public Strassen(){
        //我们在这里并没有构建具体的矩阵,仅仅有一个一维数组来模拟矩阵的行列;四个矩阵就需要5个数字
        //矩阵的分别为:A1[2][4];A2[4][5];A3[5][5];A4[5][3];
        p = new int[]{2,4,5,5,3};
        //用来存储相应个矩阵连乘的最小次数
        Arrays = new int[4][4];
        //存储分割点k;
        s = new int[4][4];
    } 
//你也可以自己去设计矩阵的具体情况
    public Strassen(int n,int p[]){
        this.p = new int[n+1];
        this.Arrays = new int[n][n];
        this.s = new int[4][4];
        for(int i =0;i<p.length;i++){
            this.p[i] = p[i];
        }
    }
    public void martixChain(){
        int n = Arrays.length;
        //当矩阵链的长度为1时:即矩阵个数只有的一个情况
        for(int i =0;i<n;i++){
            Arrays[i][i] = 0;
        }
        //这里的r是用来控制矩阵链的长度与
        for(int r = 2;r<=n;r++){
            //这里的i表示第i个矩阵
            for(int i =0;i<=n-r;i++){
                //j表示第j个矩阵:两者合起来的意思就是:A[i:j]从矩阵连乘Ai....Aj;
                int j = i+r-1;
                //这里原本等于Arrays[i][i]+Arrays[i+1][j]+p[i]p[i+1]*p[j+1]
                //p[i]表示第i个矩阵的行数,p[i+1]表示第i个矩阵的列数,p[j+1]表示第j个矩阵的列数
                Arrays[i][j] = Arrays[i+1][j]+p[i]*p[i+1]*p[j+1];
                //表示连乘的分割点是i;即将连乘的矩阵分为第一个矩阵自成一队,后面剩余的的矩阵成一队;
                s[i][j] = i;
                //循环选出连乘数目最小的情况:例如:当n=6,r=3:i=0时
                //A1*A2*A3:这三个矩阵连乘情况可以为:(A1*A2)*A3或者A1*(A2*A3)选出乘积次数最小的情况
                for(int k =i+1;k<j;k++){
                    int temp = Arrays[i][k]+Arrays[k+1][j]+p[i]*p[k+1]*p[j];
                    if(temp<Arrays[i][j]){
                        Arrays[i][j] = temp;
                        s[i][j] = k;
                    }
                }
            }
            
        }
    }
    public void traceBack(int a,int b){  
        if(a<b){  
            traceBack(a, s[a][b]);  
            traceBack(s[a][b]+1, b);  
            System.out.println("先把A"+a+"到A"+s[a][b]+"括起来,在把A"+(s[a][b]+1)+"到A"+b+"括起来,然后把A"+a+"到A"+b+"括起来");  
        }  
    } 
      public void printM(){
          int length = Arrays.length;
            for (int i=0;i<length;i++){
                for (int j=0;j<length;j++){
                    System.out.print(Arrays[i][j]+ "   ");
                }
                System.out.println();
            }
        }
    //测试
    public static void main(String[] args) {
        Strassen stra = new Strassen();
        stra.martixChain();
        stra.traceBack(0, 3);
        stra.printM();
    }
}

下图摘自:陈斌彬的技术博客
图示主要是为了展示具体的求解过程,使用了6个矩阵连乘,方便大家理解

计算过程图.jpg

第三种解法:备忘录法:
备忘录方法就是动态规划算法的变形。和动态规划算法一样,备忘录方法也会使用表格保存已经解决子问题的答案,在需要时,只需要简单的查看;和动态规划不同的是,备忘录的递归算法是第定向下的,而动态规划算法则是自底向上递归的,因此备忘录的控制结构和直接递归方法的控制结构是一样的,却别在于备忘录方法为每一个已经解决的子问题建立了备忘录以备需要时查看,避免相同的子问题重复求解;

/**
*备忘录法求解矩阵连乘问题 * 这个方法是初始化存储矩阵相乘次数的数
*备忘录法求解矩阵连乘问题
*/ 
//这个方法是初始化存储矩阵相乘次数的数组
public int MemoizedMatrixChain(int n){
          for(int i =0;i<n;i++){
              for(int j =i;j<n;j++){
                 Arrays[i][j] = 0; 
              }
          }
          return LookUpChain(0,n-1);
      }
      public int LookUpChain(int i,int j){
          //如果相应位置的相乘次数已经计算出来了,直接返回计算次数;
          if(Arrays[i][j]>0){
              return Arrays[i][j];
          }
          //表示只有一个矩阵时的情况
          if(i==j){
              return 0;
          }
          //矩阵连乘A[i:j]分割成A[i:i]和A[i+1:j]两个矩阵的全括号方式;
          int temp = LookUpChain(i,i)+LookUpChain(i+1,j)+p[i]*p[i+1]*p[j+1];
          //记录当前的分割点
          s[i][j] = i;
          //
          for(int k =i+1;k<j;k++){
                int t = LookUpChain(i,k)+LookUpChain(k+1,j)+p[i]*p[k+1]*p[j];
                if(t<temp){
                    temp = t;
                    s[i][j] = k;
                }
          }
          Arrays[i][j] = temp;
          return temp;
      }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,187评论 0 18
  • 树形动态规划,顾名思义就是树+DP,先分别回顾一下基本内容吧:动态规划:问题可以分解成若干相互联系的阶段,在每一个...
    Mr_chong阅读 1,396评论 0 2
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,663评论 0 33
  • 一 作...
    王震翔阅读 312评论 0 1
  • 班主任说儿子喜欢抬杠 儿子说:“是啊,俺爸是杠子馍,俺妈是杠头,我是小杠子,以后我生个儿子叫单杠,生个女儿叫双杠!...
    郭晓光阅读 236评论 3 2