动态规划——矩阵连乘的问题

34 篇文章 0 订阅
22 篇文章 2 订阅

《问题的引出》(《算法设计与分析》,王晓东编著,清华大学出版社20081月第2版。

看下面一个例子,计算三个矩阵连乘{A1,A2,A3};维数分别为10*100 , 100*5 , 5*50

按此顺序计算需要的次数((A1*A2)*A3):10X100X5+10X5X50=7500次

按此顺序计算需要的次数(A1*(A2*A3)):10X5X50+10X100X50=75000次

所以问题是:如何确定运算顺序,可以使计算量达到最小化。

枚举显然不可,如果枚举的话,相当于一个“完全加括号问题”,次数为卡特兰数,卡特兰数指数增长,必然不行。

《建立递归关系》

子问题状态的建模(很关键):令m[i][j]表示第i个矩阵至第j个矩阵这段的最优解。

显然如果i=j,则m[i][j]这段中就一个矩阵,需要计算的次数为0;

     如果i>j,则m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]Xp[k]Xp[j]},其中k,在i与j之间游荡,所以i<=k<j ;

代码实现时需要注意的问题:计算顺序!!!

因为你要保证在计算m[i][j]查找m[i][k]和m[k+1][j]的时候,m[i][k]和m[k+1][j]已经计算出来了。

 

 

观察坐标的关系如下图:

实习代码1(java):

package test1;

public class MatrixMultiply {

 /**
  * @zhouhong
  *  下面是矩阵连乘问题的动态规划算法
  *  假设有6个矩阵:
  *  A1 A2 A3 A4 A5 A6
  *  30*35 35*15 15*5 5*10 10*20 20*25 则matrixChain为
  *  {30,35,15,5,10,20,25} 结果为
  *  ((A1*(A2*A3))*((A4*A5)*A6))
  */
 
 public static void traceback(String A[],int[][] s, int i, int j){
  //s[i][j]记录了断开的位置,即计算A[i:j]的加括号方式为:
  //(A[i:k])*(A[k+1:j])也就是(A[i:s[i][j]])*(A[s[i][j]+1;j])
  if(i == j)
   System.out.print(A[i-1] + " ");
  else
  {
   System.out.print("(");
   traceback(A,s,i,s[i][j]);//递归打印A[i:s[i][j]]的加括号方式
   traceback(A,s,s[i][j]+1,j);
   System.out.print(")");
  }

  //System.out.println("Mutiply A(" + i + "," + s[i][j] + ") and A(" + (s[i][j]+1)+ "," + j + ")");
  
 }
 
 public static void matrixChain(int []p, int [][]m, int [][]s)
 {
  int n = p.length - 1;
  for( int i = 0; i <= n; i++)
   m[i][i] = 0;//单个矩阵相乘次数为0
  for(int r = 2; r <= n; r++ )//r为连乘的矩阵的个数
  {
   for(int i = 1;i <= n - r + 1; i++)//i就是连续r个矩阵的第一个
   {
    int j = i + r -1;//j就是连续r个矩阵的最后一个
    m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];//初始化m[i][j],即为:A[i:i]*A[i+1,j], s[i][j]=i (也就是:k= i);
    s[i][j] = i;//初始化s[i][j]
    //求m[i][j],m[i][j]就是Ai...Aj这j-i+1个矩阵连乘需要的最少的乘法次数
    for(int k = i + 1; k < j; k++){
     int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
     if(t < m[i][j])//寻找最小指
     {
      m[i][j] = t;
      s[i][j] = k;//记录划分标记
     }  
    }
   }
  }
  //n个矩阵连乘的最少相乘次数
  System.out.println("给定的"+n+"个矩阵连乘的最少相乘的次数为:"+m[1][n]);
//  printM(m);
//  printS(s);
 }
 
/* private static void printM(int[][] m){
  for(int i = 1; i < 7; i++){
   for(int j = 1; j < 7; j++){
    System.out.println(m[i][j] + "\t");
   }
   System.out.println();
  }
 }
 
 private static void printS(int[][] s){
  for(int i = 1; i < 7; i++){
   for(int j = 1; j < 7; j++){
    System.out.println(s[i][j] + "\t");
   }
   System.out.println();
  }  
 }
 */
 public static void main(String[] args) {
  int[][] m = new int[7][7];
  int[][] s = new int[7][7];
  int[] p = new int[] {30,35,15,5,10,20,25};
  String[] A = new String[] {"A1","A2","A3","A4","A5","A6"};
  matrixChain(p,m,s);
  traceback(A,s,1,6);
 }
}

输出结果:

给定的6个矩阵连乘的最少相乘的次数为:15125
((A1 (A2 A3 ))((A4 A5 )A6 ))

 

实习代码2(C++):

/*
问题描述:计算n个矩阵连乘所需的最少乘法次数
*/
#include <iostream>
#include <string>
using namespace std;
int Matrix_chain_Multiply(int p[],int n,int m[][8],int s[][8])
{
    //这里的n是数组p[]的元素的个数,比矩阵个数多了一个
    n -= 1;
    int i,j,r,k,q;
    for(i = 1; i <= n; i++)
        m[i][i] = 0;//单个矩阵相乘次数为0
    for(r = 2; r <= n; r++)
    {
        for(i = 1; i <= n -r + 1; i++)
        {
            j = i + r -1;
            m[i][j] = 9999999;
            //m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
            //s[i][j] = i;
            for(k = i; k < j; k++)
            {
                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;
                }
            }
        }
    }
    return m[1][n];
}

void Print_Matrix_Chain(string A[], int s[][8],int i,int j)
{
    //输出最优解
    if(i == j)
        cout<<A[i-1]<<" ";
    else
    {
        cout<<"(";
        Print_Matrix_Chain(A,s,i,s[i][j]);
        Print_Matrix_Chain(A,s,s[i][j]+1,j);
        cout<<")";
    }
}

int main()
{
    int p[7] = {30,35,15,5,10,20,25};
    int m[8][8],s[8][8];
    int min = Matrix_chain_Multiply(p,7,m,s);
    cout<<"上述6个矩阵连乘,最少需要做"<<min<<"次乘法运算"<<endl;
    string A[6] = {"A1","A2","A3","A4","A5","A6"};
    cout<<"最优的全加括号形式为:";
    Print_Matrix_Chain(A,s,1,6);
    cout<<endl;
    return 0;
}

 输出结果:

上述6个矩阵连乘,最少需要做15125次乘法运算
最优的全加括号形式为:((A1 (A2 A3 ))((A4 A5 )A6 ))

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值