动态规划算法解矩阵连乘问题
- 格式:doc
- 大小:63.00 KB
- 文档页数:5
矩阵连乘问题的算法
一、矩阵连乘问题
矩阵连乘问题是指在矩阵计算中,给定n个矩阵,求这n个矩阵的连乘积的最优解问题。
矩阵连乘问题既可以用于组合优化,也可以用于信息处理系统中查找最优路径的搜索算法。
它是最基本的组合优化问题。
二、矩阵连乘问题的算法
1. 动态规划法:动态规划法是求解矩阵连乘问题的常用算法。
它采用递归方法,将原问题分解为若干个子问题,然后求出各子问题的最优解,最后组合出原问题的最优解。
2. 贪心算法:贪心算法是一种经典的最优化算法,也可以用于求解矩阵连乘问题,即通过某种启发式规则,在每一步中都使最优决策,最终得到最优解。
3. 分支定界法:分支定界法是一种由搜索算法和界定法相结合而成的最优化算法,也可以用于求解矩阵连乘问题。
该算法按照树状的层次结构,向下搜索一个在每一步骤都使得当前最优的路径,然后上溯形成最优解。
4. 模拟退火算法:模拟退火算法是一种搜索算法,它可以用于求解矩阵连乘问题。
它采用一种模拟物理过程的原理,通过不断地改变解的状态,以求出相对最优解。
- 1 -。
矩阵连乘问题(动态规划算法)动态规划算法思想简介:将⼀个问题分解为多个⼦问题,这点和分治法类似,但是每个⼦问题不是独⽴的⽽是相互联系的,所以我们在求解每个⼦问题的时候可能需要重复计算到其他的⼦问题,所以我们将计算过的⼦问题的解放进⼀个表中,这样就能避免了重复计算带来的耗费,这就是动态规划的基本思想;⼀般地,动态规划思想⼀般⽤来解最优化问题,主要分为以下四个步骤:(1)找出最优解的性质,并刻画其结构特征;(2)递归地定义最优值;(3)以⾃底向上的⽅式计算出最优值;(4)根据计算得到的最优值时得到的信息,构造最优解;同时,问题的最优⼦结构性质也是该问题可⽤动态规划算法求解的显著特征,这⾥的最优⼦结构性质即指:问题的最优解也即代表着它的⼦问题有了最优解;问题描述:分析过程如下:(1)分析最优⼦结构的性质:(2)分析递归关系,以及利⽤⾃底向上的⽅式进⾏计算:(3)获取最优值和最优解:代码如下:#ifndef MATRIX_CHAIN_H#define MATRIX_CHAIN_Hvoid matrix_chain(int *p, int n, int **m, int **s);void traceback(int i, int j, int **s);#endif#include <iostream>#include "matrix_chain.h"using namespace std;//利⽤动态规划算法获取最优值void matrix_chain(int *p, int n, int **m, int **s) //p:各个矩阵的列数,n:矩阵个数,m:m[i:j]矩阵i到j的相乘次数,s:对应的分开位置{for (int i = 0; i < n; i++){m[i][i] = 0;}for (int r = 2; r <= n; r++){for (int i = 0; i < n - 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;}}}}}//利⽤s[i][j]获取最优解void traceback(int i, int j, int **s){if (i == j)return;traceback(i, s[i][j], s);traceback(s[i][j] + 1, j, s);cout << "Multiply A" << i << " , " << s[i][j];cout << "and A" << (s[i][j] + 1) << " , " << j << endl;}#include <iostream>#include "matrix_chain.h"using namespace std;int main(void){int matrix_num = 0; //矩阵个数cout << "请输⼊矩阵个数:" << endl;cin >> matrix_num;int **m = new int *[matrix_num];for (int i = 0; i < matrix_num; i++)m[i] = new int[matrix_num];int **s = new int *[matrix_num];for (int i = 0; i < matrix_num; i++)s[i] = new int[matrix_num];int *p = new int[matrix_num];cout << "请输⼊各矩阵的列数:" << endl;for (int i = 0; i < matrix_num; i++){cin >> p[i];}matrix_chain(p, matrix_num, m, s);traceback(0, matrix_num - 1, s);system("pause");return1;}可结合我的另⼀篇关于贪⼼算法的博客进⾏⽐较,了解这两者的区别;。
动态规划法解矩阵连乘问题实验内容给定n个矩阵{A1,A2,….An},其中Ai与Ai+1是可乘的,i=1,2,3。
,n-1。
我们要计算这n个矩阵的连乘积。
由于矩阵乘法满足结合性,故计算矩阵连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则我们可依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
解题思路将矩阵连乘积A(i)A(i+1)…A(j)简记为A[i:j],这里i <= j 。
考察计算A[i:j]的最优计算次序。
设这个计算次序在矩阵A(k)和A(k+1)之间将矩阵链断开,i <= k < j, 则其相应完全加括号方式为(A(i)A(i+1) …A(k)) * (A(k+1)A(k+2) …A(j))。
特征:计算A[i:j]的最优次序所包含的计算矩阵子链A[i:k]和A[k+1:j]的次序也是最优的。
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。
设计算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, …,n当i < j 时,m[i,j] = m[i,k] + m[k+1,j] + p(i-1)p(k)p(j) 这里A(i)的维数为p(i-1)*(i)( 注:p(i-1)为矩阵A(i)的行数,p(i)为矩阵A[i]的列数)实验实验代码#in elude <iostream>#in elude <vector>using n amespaee std ;class matrix_cha in{public:matrix_eha in(const vector <int> & c){ cols = c ;count = cols.size (); mc.resize (co unt);s.resize (co unt);for (i nt i = 0; i < count; ++i) { mc[i].resize (co unt); s[i].resize (co unt);}for (i = 0; i < count; ++i) { for (int j = 0; j < count; ++j) { mc[i][j] = 0 ;s[i][j] = 0 ;//记录每次子问题的结果void lookup_cha in () {__lookup_cha in (1, count - 1);min_count = mc[1][co unt - 1];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, count - 1);}//使用普通方法进行计算void calculate () {int n = count - 1; //矩阵的个数// r表示每次宽度// i,j表示从从矩阵i到矩阵j// k表示切割位置for (i nt r = 2; r <= n; ++ r) {for (int i = 1; i <= n - r + 1; ++ i) {int j = i + r - 1 ;//从矩阵i到矩阵j连乘,从i的位置切割,前半部分为0mc[i][j] = mc[i+1][j] + cols[i-1] * cols[i] * cols[j];s[i][j] = i ;for (int k = i + 1; k < j; ++ k) {int temp = mc[i][k] + mc[k + 1][j] +cols[i-1] * cols[k] * cols[j];if (temp < mc[i][j]) {mc[i][j] = temp ;s[i][j] = k ;}} // for k} // for i} // for rmin_count = mc[1][ n];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, n);private:int __lookup_cha in (int i, i nt j) {//该最优解已求出,直接返回if (mc[i][j] > 0) {return mc[i][j];}if (i == j) {return 0 ; //不需要计算,直接返回}//下面两行计算从i到j按照顺序计算的情况int u = __lookup_cha in (i, i) + __lookup_cha in (i + 1, j)+ cols[i-1] * cols[i] * cols[j];s[i][j] = i ;for (int k = i + 1; k < j; ++ k) {int temp = __lookup_cha in (i, k) + __lookup_cha in(k + 1, j)+ cols[i - 1] * cols[k] * cols[j];if (temp < u) {u = temp ;s[i][j] = k ;}}mc[i][j] = u ;return u ;}void __trackback (int i, i nt j) {if (i == j) {return ;}__trackback (i, s[i][j]);__trackback (s[i][j] + 1, j);cout <<i << "," << s[i][j] << " " << s[i][j] + 1 << "," << j << endl;}private:vector<int> cols ; // 列数int count ; // 矩阵个数+ 1vector<vector<int> > mc; //从第i个矩阵乘到第j个矩阵最小数乘次数vector<vector<int> > s; //最小数乘的切分位置int min_count ; //最小数乘次数};int mai n(){//初始化con st i nt MATRIX_COUNT = 6 ;vectorvi nt> c(MA TRIX_COUNT + 1);c[0] = 30 ;c[1] = 35 ;c[2] = 15 ;c[3] = 5 ;c[4] = 10 ;c[5] = 20 ;c[6] = 25 ;matrix_cha in me (c); // mc.calculate (); mc」o okup_cha in (); return 0 ;}实验结果实验验证从s 可知计算顺序为((A1(A2A3))((A4A5))A6))实验总结在这次实验中懂得了动态规划法运用方法和解题思路的重要性,在这个程序中如何 建立动态规划的过程建立递归过程 保存已解决的子问题答案。
算法设计与分析——矩阵连乘问题(动态规划)⼀、问题描述引出问题之前我们先来复习⼀下矩阵乘积的标准算法。
int ra,ca;//矩阵A的⾏数和列数int rb,cb;//矩阵B的⾏数和列数void matrixMultiply(){for(int i=0;i<ra;i++){for(int j=0;j<cb;j++){int sun=0;for(int k=0;k<=ca;k++){sum+=a[i][k]*b[k][j];}c[i][j]=sum;}}}给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
加括号的⽅式对计算量有很⼤的影响,于是⾃然地提出矩阵连乘的最优计算次序问题,即对于给定的相继n个矩阵,如何确定矩阵连乘的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
⼆、问题分析矩阵连乘也是Catalan数的⼀个常⽤的例⼦,关于时间复杂度的推算需要参考离散数学关于Catalan的内容。
下⾯考虑使⽤动态规划法解矩阵连乘积的最优计算次序问题。
1、分析最优解的结构问题的最优⼦结构性质是该问题可以⽤动态规划求解的显著特征!!!2、建⽴递归关系3、计算最优值public static void matrixChain(int n) {for (int i = 1; i <= n; i++) {m[i][i] = 0;}for (int r = 2; r <= n; r++) {//i与j的差值for (int i = 1; i <= n - 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;}}}}}4、构造最优解public static void traceback(int i, int j) {if (i == j) {System.out.printf("A%d", i); // 输出是第⼏个数据return;}System.out.printf("(");traceback(i, s[i][j]);// 递归下⼀个数据System.out.printf(" x ");traceback(s[i][j] + 1, j);System.out.printf(")");}三、总结。
动态规划算法解矩阵连乘问题一、实验目的通过上机实验,要求掌握动态规划算法的问题描述、算法设计思想、程序设计和算法复杂性分析等。
二、实验环境VC6.0 C++,vs2005三、实验内容1 用动态规划算法解矩阵连乘问题(1)问题的描述给定n个矩阵{A1,A2,…,A n},其中A i与A i+1是可乘的,i=1,2,…,n-1。
要算出这n个矩阵的连乘积A1A2…A n。
由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式来确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用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)为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察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。
矩阵链乘法(动态规划)
⼀题意描述:
给定由n个要相乘的矩阵构成的序列(链)<A1,A2,A3,····A n>。
由于矩阵满⾜结合律(加括号⽅式表⽰结合⽅式),不同的计算⽅式导致的求出最终计算结果的代价相异,有的花的时间很少,有的⽅式所花时间很多,那么下⾯的任务就是求出算出结果所需要的最少时间及⼀个最优解。
⼆思路分析:
设p(n)表⽰⼀串n个矩阵可能的加全部括号⽅案数。
当n=1时,只有⼀个矩阵,此时p(1)=1。
当n>=2时,⼀个加全部括号的矩阵乘积等于两个加全部括号的⼦矩阵乘积的乘积,⽽且这两个⼦乘积之间的分裂可能发⽣在第k个和第k+1个矩阵之间,其中k=1,2,····,n-1;因此可以求得递归式:
1.找局部最优解:把问题:转化成两个最优⼦问题:及
2.构造递归解:
⾸先定义m[i,j]为解决⼦问题A[i....j]的最⼩计算次数,那么解决整个问题A[1,n]所花的最⼩时间为m[1,n]。
那么递归⽅程可以写成如下形式:
为了跟踪如何构造⼀个最优解我们可以定义s[i,j]为这样的⼀个k值,在该处分裂乘积后可得⼀个最优解。
3.构造函数进⾏求解
输出最优路径的函数⾃⼰编写,经过调⽤数组s[i][j]即可。
动态规划之矩阵链相乘问题(算法导论)问题描述:给定n个矩阵序列,(A1,A2,A3,A4,...,An). 计算他们的乘积:A1A2A3...An.由于矩阵的乘法运算符合结合律,因⽽可以通过调整计算顺序,从⽽降低计算量。
样例分析:⽐如有三个矩阵分别为:A1: 10*100,A2: 100*5,A3: 5*50假如现在按照(A1A2)A3的顺序计算需要的计算量为:10*100*5+10*5*50=7500次运算。
若按照A1(A2A3)的顺序计算,需要的计算量为:100*5*50+10*100*50=75000次运算。
上⾯两种不同的运算顺序所有的计算量相差⼗倍。
因⽽,⼀种最优的计算顺序将能很⼤程度的减少矩阵连乘的运算量。
问题解析:此问题的⽬的是寻找⼀种最优的括号化⽅案。
下⾯⽤动态规划的思想来进⾏分析:1、动态规划的第⼀步:寻找最优⼦结构。
为⽅便起见,使⽤Ai..j表⽰AiAi+1...Aj的乘积结果矩阵。
对于k(i<=k<j), 计算Ai..j所需要的计算量为:Ai..k 和 Ak+1..j 以及⼆者相乘的代价和。
2、设m[i][j]为Ai..j的最优计算顺序所要花费的代价。
则其求解公式为:if i == j, m[i][j] = 0; //因为只有⼀个矩阵时计算代码为0,即不需要计算。
m[i][j]=min{m[i][k] + m[k+1][j] + Pi-1PkPj} i<=k<j3、为了能够输出求解顺序,需要保存区间中的⼀些分割点。
假如Ai..j中的最优分割点为k,则我们使⽤s[i][j]=k。
即在Ai..j 中,分别计算Ai..k 和 Ak+1..j 所⽤的计算开销最⼩。
4、采⽤⾃底向上的表格法。
依次求解矩阵长度为2,3,...,n的最优计算顺序。
算法思想:1、对m[i][i]全部初始化为0.2、在矩阵链A1..n中,依次计算长度len为2,3,...,n的m[i][j]⼤⼩。
C语⾔矩阵连乘(动态规划)详解动态规划法题⽬描述:给定n个矩阵{A1,A2....An},其中Ai与Ai+1是可以相乘的,判断这n个矩阵通过加括号的⽅式相乘,使得相乘的次数最少!以矩阵链ABCD为例按照矩阵链长度递增计算最优值矩阵链长度为1时,分别计算出矩阵链A、B、C、D的最优值矩阵链长度为2时,分别计算出矩阵链AB、BC、CD的最优值矩阵链长度为3时,分别计算出矩阵链ABC、BCD的最优值矩阵链长度为4时,计算出矩阵链ABCD的最优值动归⽅程:分析:k为矩阵链断开的位置d数组存放矩阵链计算的最优值,d[i][j]是以第i个矩阵为⾸,第j个矩阵为尾的矩阵链的最优值,i > 0m数组内存放矩阵链的⾏列信息,m[i-1]和m[i]分别为第i个矩阵的⾏和列(i = 1、2、3...)c语⾔实现代码:#include <stdio.h>#define N 20void MatrixChain(int p[N],int n,int m[N][N],int s[N][N]){int i,j,t,k;int r; //记录相乘的矩阵个数变量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;//拿到从 i 到 j 矩阵连乘的次数m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//拿到矩阵连乘断开的位置s[i][j]=i;//寻找加括号不同,矩阵连乘次数的最⼩值,修改 m 数组,和断开的位置 s 数组for(k=i+1;k<j;k++){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;}}}}}int main(void){int n,n1,m1,i,j=2;int p[N]={0}; //存储矩阵的⾏和列数组int m[N][N]={0}; //存储矩阵与矩阵相乘的最⼩次数int s[N][N]={0}; //存储矩阵与矩阵相乘断开的位置printf("请输⼊矩阵个数:\n");scanf("%d",&n);for(i=1;i<=n;i++){printf("请输⼊第%d个矩阵的⾏和列(n1*m1 格式):",i);scanf("%d*%d",&n1,&m1);if(i==1){p[0]=n1;p[1]=m1;}else{p[j++]=m1;}}printf("\n记录矩阵⾏和列:\n");for(i=0;i<=n;i++){printf("%d ",p[i]);}printf("\n");MatrixChain(p,n,m,s);printf("\n矩阵相乘的最⼩次数矩阵为:\n");for(i=1;i<=n;i++){for(j=1;j<=n;j++){printf("%d ",m[i][j]);}printf("\n");}printf("\n矩阵相乘断开的位置矩阵为:\n");for(i=1;i<=n;i++){for(j=1;j<=n;j++){printf("%d ",s[i][j]);}printf("\n");}printf("矩阵最⼩相乘次数为:%d\n",m[1][n]);return 0;}感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。
矩阵连乘问题python矩阵连乘问题是一个经典的动态规划问题,我们可以使用动态规划来解决。
假设有n个矩阵需要连乘,其中第i个矩阵的维度为d[i-1] * d[i],其中d是一个长度为n+1的数组,表示矩阵的维度。
我们可以定义一个二维数组dp,其中dp[i][j]表示从第i个矩阵到第j个矩阵的最小乘法次数。
初始化dp数组,对于i=j的情况,dp[i][j]=0;对于i>j的情况,dp[i][j]=无穷大。
接下来,我们可以使用动态规划的思想来填充dp数组。
假设我们要计算dp[i][j],我们可以枚举中间的分割点k,将问题分解为两个子问题:从i到k的矩阵连乘和从k+1到j的矩阵连乘。
则dp[i][j]的值可以通过以下方式计算:dp[i][j] = min(dp[i][k] + dp[k+1][j] + d[i-1]*d[k]*d[j]) 最后,dp[1][n]即为所求的最小乘法次数。
以下是一个使用动态规划解决矩阵连乘问题的Python代码示例: ```pythondef matrix_chain_order(d):n = len(d) - 1dp = [[float("inf")] * (n+1) for _ in range(n+1)]for i in range(1, n+1):dp[i][i] = 0for l in range(2, n+1):for i in range(1, n-l+2):j = i + l -1for k in range(i, j):cost = dp[i][k] + dp[k+1][j] + d[i-1] * d[k] * d[j]if cost < dp[i][j]:dp[i][j] = costreturn dp[1][n]```使用示例:```pythond = [10, 20, 30, 40, 30]result = matrix_chain_order(d)print(result) # 输出:30000```以上代码中,d是一个长度为n+1的数组,表示n个矩阵的维度。
动态规划之矩阵连乘【问题描述】给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
分析:矩阵链乘法问题描述:给定由n个矩阵构成的序列{A1,A2,...,An},对乘积A1A2...An,找到最⼩化乘法次数的加括号⽅法。
1)寻找最优⼦结构此问题最难的地⽅在于找到最优⼦结构。
对乘积A1A2...An的任意加括号⽅法都会将序列在某个地⽅分成两部分,也就是最后⼀次乘法计算的地⽅,我们将这个位置记为k,也就是说⾸先计算A1...Ak和Ak+1...An,然后再将这两部分的结果相乘。
最优⼦结构如下:假设A1A2...An的⼀个最优加括号把乘积在Ak和Ak+1间分开,则前缀⼦链A1...Ak的加括号⽅式必定为A1...Ak的⼀个最优加括号,后缀⼦链同理。
⼀开始并不知道k的确切位置,需要遍历所有位置以保证找到合适的k来分割乘积。
2)构造递归解设m[i,j]为矩阵链Ai...Aj的最优解的代价,则3)构建辅助表,解决重叠⼦问题从第⼆步的递归式可以发现解的过程中会有很多重叠⼦问题,可以⽤⼀个nXn维的辅助表m[n][n] s[n][n]分别表⽰最优乘积代价及其分割位置k 。
辅助表s[n][n]可以由2种⽅法构造,⼀种是⾃底向上填表构建,该⽅法要求按照递增的⽅式逐步填写⼦问题的解,也就是先计算长度为2的所有矩阵链的解,然后计算长度3的矩阵链,直到长度n;另⼀种是⾃顶向下填表的备忘录法,该⽅法将表的每个元素初始化为某特殊值(本问题中可以将最优乘积代价设置为⼀极⼤值),以表⽰待计算,在递归的过程中逐个填⼊遇到的⼦问题的解。
动态规划实现矩阵链乘法问题矩阵链乘法问题( matrix-chain multiplication problem ) (1)问题描述 给定n个矩阵的链<A 1 ,A 2 ,…,A n >,其中i=1,2,…,n,矩阵A i的维数为p i-1 ×p i。
求⼀个完全“括号化⽅案”,使得计算乘积A 1 A 2 …A n 所需的标量乘法次数最⼩ (2)最优括号化⽅案的结构特征 ⽤记号 A i,j表⽰ A i A i+1 …A j通过加括号后得到的⼀个最优计算模式,且恰好在A k与A k+1之间分开。
则“前缀”⼦链A i A i+1 …A k必是⼀个最优的括号化⼦⽅案,记为A i,k;同理“后缀”⼦链A k+1 A k+2 …A j也必是⼀个最优的括号化⼦⽅案,记为A k+1,j。
(3)⼀个递归求解的⽅案 对于矩阵链乘法问题,我们将所有对于1≤i≤j≤n确定A i A i+1 …A j的最⼩代价括号⽅案作为⼦问题。
令m[i,j]表⽰计算矩阵A i,j所需要的标量乘法的次数最⼩值,则最优解就是计算A i...n所需的最低代价就是m[1,n] 递归定义m[i,j]。
①对于i=j的情况下,显然有m=0,不需要做任何标量乘法运算。
所以,对于所有的i=1、2......n,m[i,i] = 0. ②当i < j的情况,就按照最优括号化⽅案的结构特征进⾏计算m[i,j]。
假设最优括号化⽅案的分割点在矩阵A k和A k+1之间,那么m的值就是A i...k和A k+1...j的代价加上两者量程的代价的最⼩值。
即。
该公式的假设是最优分割点是已知的,但是实际上不知道。
然⽽,k只有j-i中情况取值。
由于最优分割点k必定在i~j内取得,只需要检查所有可能的情况,找到最优解即可。
可以得出⼀个递归公式 m只是给出了⼦问题最优解的代价,但是并未给出构造最优解的⾜够信息(即分割点的位置信息)。
所以,在此基础之上,我们使⽤⼀个⼆维数组s[i,j]来保存 A i A i+1 …A j 的分割点位置k。
第一小节 动态规划问题——最短路径问题一 在正式提出动态规划法前我们先看一个数学例子:例1:在 x 1+x 2+x 3+…+x n =a 是约束条件下,求n x x x z +++= 21的极大值. 令 a x a f ==max )(1 ( 0a x ≤≤ ) )max())(max()(12x a x x a f x a f -+=-+= 令 x a x y -+=且0)(22121=---=--=x a x x x a xa xdxdy可得a x=x, 所以 x=a/2故 a a a a f 222)(2=+=同理 ))(2max()(max()(23x a x x a f x a f -+=-+=令 )(2x a x y -+=0)(222221=---=--=x a x x x a x a x dx dy 所以 a x=2x , x=a/3所以 f 3(a)=a a a a a f 331331231)(3==+=用数学归纳法可以证明:f n (a) =na , x 1=x 2=x 3=…=x n =na证明:1:n=1 …2:设f n (a) =na , x 1=x 2=x 3=…=x n =na成立,则 f n+1(a)=max(x +f n (a-x))=max()(x a n x -+)令 y=)(x a n x -+y ’=x 21xa n -2=0)(2=---x a x nx x a所以 nx=a-x ,(n+1)x=a x=1+n a f n+1(a)=1+n a +n 1+n a =a n )1(+ 我们刚才的解题策略是:“摸着石头过河”,f2 利用f1的结果,f3又利用f2的结果。
类似于游戏中的一个勇士打败了一些敌人后得到一件武器,然后去打败另一个强大一些的对手,得到一件更好的武器,接着打败更强大的敌人。
最后取得胜利。
在实际生活中,有这么一类问题,它们的活动过程可分为若干个阶段,而且在任一阶段 后的行为仅依赖于第I 阶段的过程状态,而与I 阶段之前的过程如何达到这种过程如何达到这种状态的方式无关,这样的过程就构成了一个多阶段决策过程。
矩阵连乘问题【问题】:矩阵链乘问题:给定n个矩阵{A1,A2,...,An},其中Ai与Ai+1是可乘的,i=1,2...,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
【解题】:这里我采用的是动态划分算法:设计动态规划算法的步骤。
(1)找出最优解的性质,并刻划其结构特征。
(2)递归地定义最优值。
(3)以自底向上的方式计算出最优值。
(4)根据计算最优值时得到的信息,构造最优解(由子结构的最优解得到原先大问题的最优解)。
【解题关键】:将一系列相乘的矩阵(Ai....Aj)划分为两部分;即(AiA i+1...A k)(A k+1A k+2....Aj),k的位置要保证左边括号和右边括号相乘的消耗最小。
【思路】:这里我采用两种方法来实现:(1)用三重for循环来实现:根据主对角线的方向及其右边与之平行的对角线方向,由上至下,从左到右分别求出C[i][j]的值,后面要求的值都可以根据前面所求的的值来求。
C[i][j]代表矩阵链Ai..Aj相乘的最小消耗。
其中:c[i][i]=0,i=1,2,....n求解的顺序如下:C[1][2],C[2][3],C[2][3],...,C[N-1][N],C[1][3],C[2][4]....C[N-2][N]..........C[N][N]最后得到的C[N][N]的值就是我们所求的。
(2)备忘录方法(即递归算法):将整个矩阵链分成两部分,然后在分别对两边的子矩阵链递归调用算法。
【程序代码】:两种方法都在其中:#include<iostream.h>#include<stdlib.h>#include<limits.h>#include<time.h>#define MAX_VALUE 100#define N 201 //连乘矩阵的个数(n-1)#define random() rand()%MAX_VALUE //控制矩阵的行和列的大小int c[N][N], s[N][N], p[N];int matrixchain(int n) //3个for循环实现{ for(int k=1;k<=n;k++)c[k][k]=0;for(int d=1;d<n;d++)for(int i=1;i<=n-d;i++){ int j=i+d;c[i][j]=INT_MAX;for(int m=i;m<j;m++){ int t=c[i][m]+c[m+1][j]+p[i-1]*p[m]*p[j];if(t<c[i][j]){c[i][j]=t;s[i][j]=m;}}}return c[1][n];}void Print(int s[][N],int i,int j) // 输出矩阵连乘积的计算次序{ if(i==j)cout<<"A"<<i;else{cout<<"(";Print(s,i,s[i][j]); // 左半部子矩阵连乘Print(s,s[i][j]+1,j); //左半部子矩阵连乘cout<<")";}}int lookupchain(int i,int j) //备忘录方法{if(c[i][j]>0)return c[i][j];if(i==j)return 0;int u=lookupchain(i,i)+lookupchain(i+1,j)+p[i-1]*p[i]*p[j];s[i][j]=i;for(int k=i+1;k<j;k++){int t=lookupchain(i,k)+lookupchain(k+1,j)+p[i-1]*p[k]*p[j];if(t<u){u=t;s[i][j]=k;}}c[i][j]=u;return u;}void main(){srand((int)time(NULL));for(int i=0;i<N;i++) // 随机生成数组p[],各个元素的值的范围:1~MAX_VALUE p[i]=random()+1;clock_t start,end;double elapsed;start=clock();//cout<<"Count: "<<matrixchain(N-1)<<endl; //3重for循环实现cout<<"Count: "<<lookupchain(1,N-1)<<endl; //备忘录方法end=clock();elapsed=((double)(end-start));///CLOCKS_PER_SEC;cout<<"Time: "<<elapsed<<endl;Print(s,1,N-1); //输出矩阵连乘积的计算次序cout<<endl;}【总结】:两种算法的时间复杂度均为o(n3),,随着数据量的增多,备忘录方法消耗的时间越长;我觉得是由于递归算法,随着数据量增大,调用函数的次数也增大,语句被执行的时间也越多,因此调用函数消耗的时间也增多。
动态规划算法解矩阵连乘问题一、实验目的通过上机实验,要求掌握动态规划算法的问题描述、算法设计思想、程序设计和算法复杂性分析等。
二、实验环境VC6.0 C++,vs2005三、实验内容1 用动态规划算法解矩阵连乘问题(1)问题的描述给定n个矩阵{A1,A2,…,A n},其中A i与A i+1是可乘的,i=1,2,…,n-1。
要算出这n个矩阵的连乘积A1A2…A n。
由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式来确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用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)为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察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,…,A n}(其中矩阵Ai的维数为p i-1×p i,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…A n的计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。
正如课堂中所分析,穷举法的计算量太大,它不是一个有效的算法,本实验采用动态规划算法解矩阵连乘积的最优计算次序问题。
(2)算法设计思想动态规划算法的基本思想是将待求解问题分成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,动态规划法经分解得到的子问题往往不是相互独立的,前一子问题的解为后一子问题的解提供有用的信息,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
本实验的算法思路是:1)计算最优值算法MatrixChain():建立两张表(即程序中的**m 和**s ,利用二维指针存放),一张表存储矩阵相乘的最小运算量,主对角线上的值为0,依次求2个矩阵、3个矩阵…、直到n 个矩阵相乘的最小运算量,其中每次矩阵相乘的最小运算量都在上一次矩阵相乘的最小运算量的基础上求得,最后一次求得的值即为n 个矩阵相乘的最小运算量;另一张表存储最优断开位置。
2)输出矩阵结合方式s ,自己加括号,构造出最优加括号方式。
当然也可以编程输出,这部分不作强制要求。
(3)程序设计//测试数据可以设为六个矩阵分别为//A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]//p 用来记录矩阵的行列,则p[1-7]={30,35,15,5,10,20,25}//m[i][j]用来记录第i 个矩阵乘至第j 个矩阵的最优解(即最小乘积次数) //s[][]用来记录从哪里断开,才可得到该最优解计算示例:注意上图(a )中的计算次序!(1)理解算法思想,读懂程序(2)给程序注上详细解释(3)通过矩阵s 给出最优乘积次序【程序及详解,最优乘积输出附此】程序及详解// jzlc.cpp : 矩阵连乘⎪⎩⎪⎨⎧=⨯⨯++=++=⨯⨯++=++=⨯⨯++=++=1137520103504375]5][5[]4][2[71252053510002625]5][4[]3][2[1300020153525000]5][3[]2][2[min ]5][2[541531521p p p m m p p p m m p p p m m m#include <iostream>#include <conio.h>#include <time.h>using std::cout;using std::endl;int main(){int p[]={30,35,15,5,10,20,25}; //p[0],p[1]确定A1行列数,p[1],p[2]确定A2行列数,依次类推int n=sizeof(p)/sizeof(int)-1; //自动计算矩阵个数,增加程序灵活性int i,j,k,r;long **m=new long*[n+1]; int **s=new int*[n+1];for(i=0;i<=n;i++) m[i]=new long[n+1]; //m 行列数n*n,下标都从1开始for(i=0;i<=n;i++) s[i]=new int[n+1]; //s 行列数n*n,下标都从1开始for(i=0;i<=n;i++) m[i][i]=s[i][i]=0; // 矩阵初始化// 给以下程序加上注解for (r = 2; r <= n; r++) /* 数组相乘个数 */for (i = 1; i <= n - r+1; i++) /* n行里每行要求得的值的个数 */{j=i+r-1; /* 相乘数组中最后数组的列指针 */m[i][j] = m[i+1][j]+ p[i-1]*p[i]*p[j]; /* 得到两个数组相乘的运算量*/s[i][j] = i;for (k = i+1; k < j; k++) /* 加括号方法得到的组数 */{long 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; /* 括号分割的位置 */}}}//输出矩阵m与s的上三角阵,结果应与图(b)、(c)一致,你的代码写在下面!//此处为你的输出矩阵m与s的上三角阵的代码!cout<<"图(b)显示结果如下:"<<endl;for(i=0;i<=n;i++){if (i==0){cout<<"\t";}else{cout<<i<<"\t";}}cout<<endl;for (i=1;i<=n;i++){cout<<i<<"\t";for (int j=1;j<=n;j++){if (m[i][j]>=0){cout<<m[i][j]<<"\t";}else{cout<<"\t";}}cout<<endl;}cout<<"图(c)显示结果如下:"<<endl; for(i=0;i<=n;i++){if (i==0){cout<<"\t";}else{cout<<i<<"\t";}}cout<<endl;for (i=1;i<=n;i++){cout<<i<<"\t";for (int j=1;j<=n;j++){if (s[i][j]>=0){cout<<s[i][j]<<"\t";}else{cout<<"\t";}}cout<<endl;}return 0;}输出结果2 算法时间复杂度与空间复杂度分析时间复杂度分析:首先分析该算法有三大循环块,第一块初始化m,第二块初始化s,第三块是对出m和s的值,第四块是输出表(b),第五块是输出表(c)。
分别求出时间复杂度,第一块为O(6),第二块为O(6),第一块最大为O(5*5*5)=O(125)第二块为O(6*6)=O(36)第三块为O(6*6)=O(36)因此时间复杂度T=O(6)+O(6)+O(125)+0(36)+O(36)=O(125)空间复杂度分析:因为此算法时间复杂度为O(5^3),因此空间复杂度为O(1)。
3 教师评分。