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

前言:今天接着学习动态规划算法,学习如何用动态规划来分析解决矩阵链乘问题。首先回顾一下矩阵乘法运算法,并给出C++语言实现过程。然后采用动态规划算法分析矩阵链乘问题并给出C语言实现过程。

1、矩阵乘法

 

  

 

 

 

  

  从定义可以看出:只有当矩阵A的列数与矩阵B的行数相等时A×B才有意义。一个m×r的矩阵A左乘一个r×n的矩阵B,会得到一个m×n的矩阵C。在计算机中,一个矩阵说穿了就是一个二维数组。一个m行r列的矩阵可以乘以一个r行n列的矩阵,得到的结果是一个m行n列的矩阵,其中的第i行第j列位置上的数等于前一个矩阵第i行上的r个数与后一个矩阵第j列上的r个数对应相乘后所有r个乘积的和。采用C++语言实现完整的两个矩阵乘法,程序如下所示:

#include<iostream>
#include<cstdlib>
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],int B[B_ROWS][B_COLUMNS],int C[A_ROWS][B_COLUMNS])
{
    if(A_COLUMNS!=B_ROWS)
    {
        cout<<"incompatible dimensions"<<endl;
        exit(1);
    }
    int i,j,k;
    for(i=0;i<A_ROWS;i++)
        for(j=0;j<B_COLUMNS;j++)
    {
        C[i][j]=0;
        for(k=0;k<A_COLUMNS;k++)
            C[i][j]+=A[i][k]*B[k][j];
    }
}

int main()
{
    int C[A_ROWS][B_COLUMNS];
    int A[A_ROWS][A_COLUMNS]={{1,2},{3,4},{5,6}};
    int B[B_ROWS][B_COLUMNS]={1,2,3,4,5,6};
    matrix_multiply(A,B,C);
    int i,j;
    for(i=0;i<A_ROWS;i++)
    {
        for(j=0;j<B_COLUMNS;j++)
            cout<<C[i][j]<<" ";
        cout<<endl;
    }
}

2、矩阵链乘问题描述

  给定n个矩阵构成的一个链<A1,A2,A3,.......An>,其中i=1,2,...n,矩阵A的维数为pi-1pi,对乘积 A1A2...A以一种最小化标量乘法次数的方式进行加全部括号。

  注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。

3、动态规划分析过程

1)最优加全部括号的结构

  动态规划第一步是寻找一个最优的子结构。假设现在要计算AiAi+1....Aj的值,计算Ai...j过程当中肯定会存在某个k值(i<=k<j)将Ai...j分成两部分,使得Ai...j的计算量最小。分成两个子问题Ai...k和Ak+1...j,需要继续递归寻找这两个子问题的最优解。

  有分析可以到最优子结构为:假设AiAi+1....Aj的一个最优加全括号把乘积在Ak和Ak+1之间分开,则Ai..k和Ak+1..j也都是最优加全括号的。

2)一个递归解

  设m[i,j]为计算机矩阵Ai...j所需的标量乘法运算次数的最小值,对此计算A1..n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:

当i==j时:m[i,j] = 0,(此时只包含一个矩阵)

当i<j 时:从步骤1中需要寻找一个k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)。

3)计算最优代价

  虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2.....n。输入序列为:p=<p0,p1,...pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下:

MAXTRIX_CHAIN_ORDER(p)
  n = length[p]-1;
  for i=1 to n
      do m[i][i] = 0;
  for t = 2 to n  //t is the chain length
       do for i=1 to n-t+1
                     j=i+t-1;
                     m[i][j] = MAXLIMIT;
                     for k=i to j-1
                            q = m[i][k] + m[k+1][i] + qi-1qkqj;
                            if q < m[i][j]
                               then m[i][j] = q;
                                    s[i][j] = k;
  return m and s;

C++代码:

#include<iostream>
using namespace std;
#define N 6
#define MAXVALUE 100000000
void matrix_chain_order(int *p,int m[N+1][N+1],int s[N+1][N+1])
{
    int i,j,l,k;
    for(i=1;i<=N;i++)
        m[i][i]=0;
    for(l=2;l<=N;l++)
    {
        for(i=1;i<=N-l+1;i++)
        {
            j=i+l-1;
            m[i][j]=MAXVALUE;
            for(k=i;k<=j-1;k++)
            {
                int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                if(temp<m[i][j])
                {
                    m[i][j]=temp;
                    s[i][j]=k;
                }
            }
        }
    }
}

void print_optimal_parens(int s[N+1][N+1],int i,int j)
{
    if(i==j)
        cout<<"A"<<i;
    else
    {
        cout<<"(";
        print_optimal_parens(s,i,s[i][j]);
        print_optimal_parens(s,s[i][j]+1,j);
        cout<<")";
    }
}

int main()
{
    int p[N+1] = {30,35,15,5,10,20,25};
    int m[N+1][N+1]={0};
    int s[N+1][N+1]={0};
    int i,j;
    matrix_chain_order(p,m,s);
    cout<<"m value is: "<<endl;
    for(i=1;i<=N;++i)
    {
        for(j=1;j<=N;++j)
            cout<<m[i][j]<<" ";
        cout<<endl;
    }
    cout<<"s value is: "<<endl;
    for(i=1;i<=N;++i)
    {
        for(j=1;j<=N;++j)
            cout<<s[i][j]<<" ";
        cout<<endl;
    }
    cout<<"The result is:"<<endl;
    print_optimal_parens(s,1,N);
    return 0;
}

时间: 2024-10-19 09:04:58

第十五章 动态规划——矩阵链乘法的相关文章

第十五章 动态规划——钢条切割

前言:动态规划的概念 动态规划(dynamic programming)是通过组合子问题的解而解决整个问题的.分治算法是指将问题划分为一些独立的子问题,递归的求解各个问题,然后合并子问题的解而得到原问题的解.例如归并排序,快速排序都是采用分治算法思想.本书在第二章介绍归并排序时,详细介绍了分治算法的操作步骤,详细的内容请参考:http://www.cnblogs.com/Anker/archive/2013/01/22/2871042.html.而动态规划与此不同,适用于子问题不是独立的情况,也

第十五章 动态规划——最长公共子序列

1.基本概念 一个给定序列的子序列就是该给定序列中去掉零个或者多个元素的序列.形式化来讲就是:给定一个序列X={x1,x2,--,xm},另外一个序列Z={z1.z2.--,zk},如果存在X的一个严格递增小标序列<i1,i2--,ik>,使得对所有j=1,2,--k,有xij = zj,则Z是X的子序列.例如:Z={B,C,D,B}是X={A,B,C,B,D,A,B}的一个子序列,相应的小标为<2,3,5,7>.从定义可以看出子序列直接的元素不一定是相邻的. 公共子序列:给定两个

第十五章 动态规划——最优二叉搜索树

1.前言: 接着学习动态规划方法,最优二叉查找树问题.二叉查找树参考http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html.如果在二叉树中查找元素不考虑概率及查找不成功的情况下,可以采用红黑树或者平衡二叉树来搜索,这样可以在O(lgn)时间内完成.而现实生活中,查找的关键字是有一定的概率的,就是说有的关键字可能经常被搜索,而有的很少被搜索,而且搜索的关键字可能不存在,为此需要根据关键字出现的概率构建一个二叉树.比如中文输入法字库中

【算法导论】动态规划之矩阵链乘法

       所谓矩阵链乘法是指当一些矩阵相乘时,如何加括号来改变乘法顺序从而来降低乘法次数.例如有三个矩阵连乘:A1*A2*A3,其维数分别为:10*100,100*5,5*50.如果按照((A1*A2)*A3)来计算的话,求(A1*A2)要10*100*5=5000次乘法,再乘以A3需要10*5*50=2500次乘法,因此总共需要7500次乘法.如果按照(A1*(A2*A3))来计算的话,求(A2*A3)要100*5*50=25000次乘法,再乘以A1需要10*100*50=50000次乘法

第十五章 接口[《.net框架程序设计》读书笔记]

.net框架|笔记|程序|设计 第十五章 接口 摘要: 接口的应用及完全限定名方式定义接口的应用. 一. 接口与继承 l C#支持单实现继承和多接口继承 l 接口中可以定义:事件.无参属性(属性).含参属性(索引器):C#不允许接口定义任何静态成员(CLR却允许定义静态成员):CLR不允许接口定义实例字段和构造器. l 缺省为public abstract 方法,但不可用任何修饰符进行修饰(包括public) l 将值类型转换为接口类型(假设其实现了某个接口),则值类型被装箱为引用类型,以调用其

第十五章之(三)RTTI

前言:刚找到新工作,这两天忙着搬家,添加一些日用品,好方便日后使用,浪费了蛮多时间的.所以到现在才补上这一章节. 之前跳过了RTTI,去看下一部分的类型转换运算符,被dynamic_cast搞的很头晕(主要是因为没搞懂为什么用这个),只能回头补上这一节,这时才发现,这一节是不能跳过的. 另外,这小节说实话,蛮难理解的(主要是指其原理,和为什么要有dynamic_cast这个类型转换运算符),这里简单的来概括,就是让指针只有在能安全使用转换后的类方法的情况下,才会被强制转换. ----------

Flash基础理论课 第十五章 3D基础Ⅰ

返回"Flash基础理论课 - 目录" 前面我们做的一切都是二维的(有时只有一维),但是已经可以做出非常酷的东东了.现在,将它们带入到下一个等级. 创建 3D 图形总是那么另人兴奋.新加入的这个维度似乎将物体真正地带入到了生活中.如何在Flash 中实现 3D 在无数的书籍和教学软件中都有介绍.但是我不打算跳过这些内容,我们会很快地将所有基础的知识讲完.随后,将前面章节中讨论的运动效果放到三维空间中.说得详细些,将给大家介绍速度,加速度,摩擦力,反弹,屏幕环绕,缓动,弹性运动,坐标旋转

第十五章-数据访问部件的应用及编程(一)(1)

在这一章里我们主要介绍Delphi的数据访问部件的层次结构.多部件之间的关系.部件的属性.方法.事件以及各部件的应用.这些部件包括: ● TSession部件 ● 数据集部件(TTable和TQuery) ● TDatasource部件 ● 字段对象TField ● 字段编辑器的使用 ● TReport部件和TBatchMove部件 我们对这些部件的属性.方法和事件进行一般性的描述,读者在实际使用Delphi开发应用程序时,还可以通过联机帮助获得有关部件更详细的信息. 15.1 Delphi数据

WF从入门到精通(第十五章):工作流和事务

学习完本章,你将掌握: 1.了解传统的事务模型以及这种模型在哪些地方适合去使用,哪些地方不适合使用 2.懂得在哪些地方不适合传统的事务以及什么时候是补偿事务的恰当时机 3.看看怎样回滚或补偿事务 4.看看怎样修改默认的补偿顺序 如果你是写软件的,你迟早需要去理解事务处理.事务处理(transactional processing)在这个意义上是指写那些把信息记录到一个持久化资源的软件,这些持久化资源如数据库.Microsoft消息队列(它在底层使用了一个数据库).带事务文件系统的Windows