矩阵连乘 动态规划(转载)
2010-10-13 08:50
337 查看
一、问题描述
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要算出这n个矩阵的连乘积A1A2…An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。完全加括号的矩阵连乘积可递归地定义为:
(1)单个矩阵是完全加括号的;
(2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)。
例如,矩阵连乘积A1A2A3A4有5种不同的完全加括号的方式:(A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)。每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。若A是一个p×q矩阵,B是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。
为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察3个矩阵{A1,A2,A3}连乘的情况。设这三个矩阵的维数分别为10×100,100×5,5×50。加括号的方式只有两种:((A1A2)A3),(A1(A2A3)),第一种方式需要的数乘次数为10×100×5+10×5×50=7500,第二种方式需要的数乘次数为100×5×50+10×100×50=75000。第二种加括号方式的计算量时第一种方式计算量的10倍。由此可见,在计算矩阵连乘积时,加括号方式,即计算次序对计算量有很大的影响。于是,自然提出矩阵连乘积的最优计算次序问题,即对于给定的相继n个矩阵{A1,A2,…,An}(其中矩阵Ai的维数为pi-1×pi,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…An的计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。
穷举搜索法的计算量太大,它不是一个有效的算法,本实验采用动态规划算法解矩阵连乘积的最优计算次序问题。
二、算法思路
动态规划算法的基本思想是将待求解问题分成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,动态规划法经分解得到的子问题往往不是相互独立的,前一子问题的解为后一子问题的解提供有用的信息,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
本实验的算法思路是:
1、计算最优值算法MatrixChain():建立两张表(即程序中的**m和**s,利用二维指针存放),一张表存储矩阵相乘的最小运算量,主对角线上的值为0,依次求2个矩阵、3个矩阵…、直到n个矩阵相乘的最小运算量,其中每次矩阵相乘的最小运算量都在上一次矩阵相乘的最小运算量的基础上求得,最后一次求得的值即为n个矩阵相乘的最小运算量;另一张表存储最优断开位置。
2、输出矩阵结合方式算法Traceback():矩阵结合即是给矩阵加括号,打印出矩阵结合方式,由递归过程Traceback()完成。分三种情况:
(1)只有一个矩阵,则只需打印出A1;
(2)有两个矩阵,则需打印出(A1A2);
(3)对于矩阵数目大于2,则应该调用递归过程Traceback()两次,构造出最优加括号方式。
三、实验源程序
建立一个矩阵的类Matrix。
Matrix.h代码
Matrix.cpp代码
#ifndef MATRIX_H
#define MATRIX_H
class Matrix
{
public:
Matrix(); //构造函数
~Matrix(); //析构函数
bool Run(); //运行接口函数
private:
int W; //记录矩阵的个数
int **m; //存放最优值,即最小运算量
int **s; //断开位置
int *p; //存放
bool Input(); //处理输入
bool MatrixChain();//计算最优值算法
void Traceback(int i,int j,int **s); //输出矩阵加括号的方式
};
#endif
#define N 50
#include <iostream.h>
#include <stdlib.h>
#include "Matrix.h"
//构造函数,作变量初始化工作,为指针分配内存空间
Matrix::Matrix()
{
W=0;
m = new int*
;
s = new int*
;
for(int i=0; i<N ; i++)
{
m[i] = new int
;
s[i] = new int
;
}
p = new int
;
}
//析构函数,释放内存
Matrix::~Matrix()
{
for(int i=0; i<N ; i++)
{
delete []m[i];
delete []s[i];
}
delete []m;
delete []s;
delete []p;
}
//处理键盘输入
bool Matrix::Input()
{
int w;
cout<<"矩阵个数:";
cin>>w;
W = w;
cout<<"输入矩阵A1维数"<<":";
cin>>p[0]>>p[1];
for(int i=2 ; i<=W ; i++)
{
int m = p[i-1];
cout<<"输入矩阵A"<<i<<"维数:";
cin>>p[i-1]>>p[i];
if(p[i-1] != m)
{
cout<<endl<<"维数不对,矩阵不可乘!"<<endl;
exit(1);
}
//cout<<endl;
}
if(p!=NULL)
return true;
else
return false;
}
//计算最优值算法
bool Matrix::MatrixChain()
{
if(NULL == p)
return false;
for(int i=1;i<=W;i++)
m[i][i]=0;
for(int r=2;r<=W;r++)
for(int i=1;i<=W-r+1;i++)
{
int j=i+r-1;
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
s[i][j] = i;
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;
}
}
}
return true;
}
//输出矩阵结合方式,加括号
void Matrix::Traceback(int i,int j,int **s)
{
if(i == j)
{
cout<<"A"<<i;
}
else if(i+1 == j)
{
cout<<"(A"<<i<<"A"<<j<<")";
}
else
{
cout<<"(";
Traceback(i,s[i][j],s);
Traceback(s[i][j]+1,j,s);
cout<<")";
}
}
bool Matrix::Run()
{
if(Matrix::Input())
{
if(Matrix::MatrixChain())
{
Matrix::Traceback(1,W,s);
cout<<endl;
return true;
}
else
return false;
}
else
return false;
}
main.cpp代码
#include "Matrix.h"
void main()
{
Matrix m;
m.Run();
}
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要算出这n个矩阵的连乘积A1A2…An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。完全加括号的矩阵连乘积可递归地定义为:
(1)单个矩阵是完全加括号的;
(2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)。
例如,矩阵连乘积A1A2A3A4有5种不同的完全加括号的方式:(A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)。每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。若A是一个p×q矩阵,B是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。
为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察3个矩阵{A1,A2,A3}连乘的情况。设这三个矩阵的维数分别为10×100,100×5,5×50。加括号的方式只有两种:((A1A2)A3),(A1(A2A3)),第一种方式需要的数乘次数为10×100×5+10×5×50=7500,第二种方式需要的数乘次数为100×5×50+10×100×50=75000。第二种加括号方式的计算量时第一种方式计算量的10倍。由此可见,在计算矩阵连乘积时,加括号方式,即计算次序对计算量有很大的影响。于是,自然提出矩阵连乘积的最优计算次序问题,即对于给定的相继n个矩阵{A1,A2,…,An}(其中矩阵Ai的维数为pi-1×pi,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…An的计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。
穷举搜索法的计算量太大,它不是一个有效的算法,本实验采用动态规划算法解矩阵连乘积的最优计算次序问题。
二、算法思路
动态规划算法的基本思想是将待求解问题分成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,动态规划法经分解得到的子问题往往不是相互独立的,前一子问题的解为后一子问题的解提供有用的信息,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
本实验的算法思路是:
1、计算最优值算法MatrixChain():建立两张表(即程序中的**m和**s,利用二维指针存放),一张表存储矩阵相乘的最小运算量,主对角线上的值为0,依次求2个矩阵、3个矩阵…、直到n个矩阵相乘的最小运算量,其中每次矩阵相乘的最小运算量都在上一次矩阵相乘的最小运算量的基础上求得,最后一次求得的值即为n个矩阵相乘的最小运算量;另一张表存储最优断开位置。
2、输出矩阵结合方式算法Traceback():矩阵结合即是给矩阵加括号,打印出矩阵结合方式,由递归过程Traceback()完成。分三种情况:
(1)只有一个矩阵,则只需打印出A1;
(2)有两个矩阵,则需打印出(A1A2);
(3)对于矩阵数目大于2,则应该调用递归过程Traceback()两次,构造出最优加括号方式。
三、实验源程序
建立一个矩阵的类Matrix。
Matrix.h代码
Matrix.cpp代码
#ifndef MATRIX_H
#define MATRIX_H
class Matrix
{
public:
Matrix(); //构造函数
~Matrix(); //析构函数
bool Run(); //运行接口函数
private:
int W; //记录矩阵的个数
int **m; //存放最优值,即最小运算量
int **s; //断开位置
int *p; //存放
bool Input(); //处理输入
bool MatrixChain();//计算最优值算法
void Traceback(int i,int j,int **s); //输出矩阵加括号的方式
};
#endif
#define N 50
#include <iostream.h>
#include <stdlib.h>
#include "Matrix.h"
//构造函数,作变量初始化工作,为指针分配内存空间
Matrix::Matrix()
{
W=0;
m = new int*
;
s = new int*
;
for(int i=0; i<N ; i++)
{
m[i] = new int
;
s[i] = new int
;
}
p = new int
;
}
//析构函数,释放内存
Matrix::~Matrix()
{
for(int i=0; i<N ; i++)
{
delete []m[i];
delete []s[i];
}
delete []m;
delete []s;
delete []p;
}
//处理键盘输入
bool Matrix::Input()
{
int w;
cout<<"矩阵个数:";
cin>>w;
W = w;
cout<<"输入矩阵A1维数"<<":";
cin>>p[0]>>p[1];
for(int i=2 ; i<=W ; i++)
{
int m = p[i-1];
cout<<"输入矩阵A"<<i<<"维数:";
cin>>p[i-1]>>p[i];
if(p[i-1] != m)
{
cout<<endl<<"维数不对,矩阵不可乘!"<<endl;
exit(1);
}
//cout<<endl;
}
if(p!=NULL)
return true;
else
return false;
}
//计算最优值算法
bool Matrix::MatrixChain()
{
if(NULL == p)
return false;
for(int i=1;i<=W;i++)
m[i][i]=0;
for(int r=2;r<=W;r++)
for(int i=1;i<=W-r+1;i++)
{
int j=i+r-1;
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
s[i][j] = i;
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;
}
}
}
return true;
}
//输出矩阵结合方式,加括号
void Matrix::Traceback(int i,int j,int **s)
{
if(i == j)
{
cout<<"A"<<i;
}
else if(i+1 == j)
{
cout<<"(A"<<i<<"A"<<j<<")";
}
else
{
cout<<"(";
Traceback(i,s[i][j],s);
Traceback(s[i][j]+1,j,s);
cout<<")";
}
}
bool Matrix::Run()
{
if(Matrix::Input())
{
if(Matrix::MatrixChain())
{
Matrix::Traceback(1,W,s);
cout<<endl;
return true;
}
else
return false;
}
else
return false;
}
main.cpp代码
#include "Matrix.h"
void main()
{
Matrix m;
m.Run();
}
相关文章推荐
- 动态规划之矩阵连乘最优化问题
- 动态规划(矩阵连乘)
- 动态规划 - 矩阵连乘问题(笔记)
- 动态规划——矩阵连乘的问题
- 算法[动态规划]-矩阵连乘问题
- 动态规划——矩阵连乘的问题
- soj 11600 Pick It 矩阵连乘模型 动态规划 acdream 1208 qj的奇怪宝具 noip 2006 energy 能量项链
- 动态规划之矩阵连乘
- 矩阵连乘 动态规划
- 矩阵连乘问题(动态规划)
- 矩阵连乘 动态规划 C#
- [动态规划]矩阵连乘
- 矩阵连乘最优解---动态规划
- 矩阵连乘----动态规划
- 动态规划——矩阵连乘的问题
- 动态规划——矩阵连乘(算法设计课题)
- [动态规划DP] 矩阵连乘
- 矩阵连乘的动态规划求解
- 动态规划解决矩阵连乘
- 动态规划——POJ1651(矩阵连乘)