实验二 用动态规划法解矩阵连乘问题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
课程名称:算法设计与分析
实验项目:实验二用动态规划法解矩阵连乘问题指导教师:***
实验位置:计算机中心二楼
*名:***
班级:计科102
学号: *********** 日期: 2012-11-7
一、实验目的
通过上机实验,要求掌握动态规划算法的问题描述、算法设计思想、程序设计和算法复杂性分析等。
二、实验环境
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 给出最优乘积次序
【程序及详解,最优乘积输出附此】
// 给以下程序加上注解
for (r = 2; r <= n; r++) /* 让r 从2开始防止例如m[1][1]和m[2][2]的情况发生 */
for (i = 1; i <= n - r+1; i++) /* 让i 逐渐增加,则可以计算m[2][j],m[3][j]....的值 */
⎪⎩⎪⎨⎧=⨯⨯++=++=⨯⨯++=++=⨯⨯++=++=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
{
j=i+r-1; /* 这个是为求m[i][j]准备的,用来计算i到j位置的矩阵的乘法次数,所以用j=i+r-1来使j的值永远处于大于i的位置。同时避免当i跟j相等时m[i][j]为0的情况*/
m[i][j] = m[i+1][j]+ p[i-1]*p[i]*p[j]; /* m用来记录i到j的矩阵之间按顺序的乘法次数,而p[i-1]*p[i]*p[j]则是矩阵计算时的 */
s[i][j] = i;
for (k = i+1; k < j; k++) /* 用k来代表矩阵隔断的位置 */
{
long t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]; /* 当矩阵在不同的位置拆开的时候所得到的数值赋给t。即i到k之间的所有隔断的方法,来求出i到k 之间乘法次数最少的位置和最少的值 */
if (t < m[i][j])
{
m[i][j] = t; /* 当所得的t的值比m[i][j]小的时候,说明t是比m[i][j]更优的解,所以把他的值赋给m[i][j].知道找出数值最小的t,即最优解 */
s[i][j] = k; /* 同时把相对最优解所断开的位置的数值赋给二维数组s[i][j] */
}
}
}
//输出矩阵m与s的上三角阵,结果应与图(b)、(c)一致,你的代码写在下面!
//此处为你的输出矩阵m与s的上三角阵的代码!
cout<<" m[i][j]:"< cout<<" 1 2 3 4 5 6"< for(i=1;i<=n;i++) { cout< for(j=1;j for(j=i;j<=n;j++) printf("%6d",m[i][j]); cout< } cout< cout<<" s[i][j]:"< cout<<" 1 2 3 4 5 6"< for(i=1;i<=n;i++) { cout< for(j=1;j for(j=i;j<=n;j++) printf("%6d",s[i][j]); cout< } _getch();