实验二:矩阵连乘问题
一、问题描述:
给定n个矩阵{A1A2…An},其中Ai和Ai+1是可乘的,考察这n个矩阵的连乘积A1A2…An。由于矩阵的乘法满足结合律,故计算矩阵的连乘积有许多不同的计算次序,而不同的计算次序,所需要计算的连乘次数也是不同的,求解连乘次数最少的矩阵连乘最优次序。
二、实验环境:
系统:Windows 10
IDE:Visual Studio 2022
编译语言:C++
三、程序代码与结果分析:
(1)分析问题:
由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
例如,矩阵连乘积A1A2A3A4有5种不同的完全加括号的方式:(A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)。每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。
(2)递推关系的建立
设计算A[i: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时,若A[i:j]的最优次序在Ak和Ak+1之间断开,i<=k<j,则:m[i][j]=m[i][k]+m[k+1][j]+pi-1pkpj。由于在计算是并不知道断开点k的位置,所以k还未定。不过k的位置只有j-i个可能。因此,k是这j-i个位置使计算量达到最小的那个位置。
综上,有递推关系如下:
(3)动态方程建立
其中Ai是Pi-1 x Pi的矩阵
接下来我们借助填表过程理解递归的过程,现在给出下列矩阵:
A1 | A2 | A3 |
10*5 | 5*50 | 50*100 |
填表过程是按对角线填写的,只利用到了二维数组的右上角一部分。
根据地推公式,我们可以知道,在i=j时m=0,所以先构造出最长的对角线部分的数据,如下表:
i/j | 1 | 2 | 3 |
1 | 0 | ||
2 | 0 | ||
3 | 0 |
现在我们继续构造,
m(1,2)=min{ m[1][1] + m[2][2] + p0p1p2 } = { 0 + 0 + 2500}=2500
m(1,3)=min{m[1][1] + m[2][3] + p0p1p3,m[1][2] + m[3][3] + p0p2p3} = {30000,52500}=30000
m(2,3)=min{ m[2][2] + m[3][3] + p1p2p3 } = { 0 + 0 + 25000}=25000
再多说一点,有时我们会遇到有多个划分,我们取最小值就可以了。
结果如下表:
i/j | 1 | 2 | 3 |
1 | 0 | 2500 | 30000 |
2 | 0 | 25000 | |
3 | 0 |
那么,我们最后如何得知是哪个地方要加括号呢?
最后的m[1:3]=m[1,1]+m[2][3]+p0p1p3,那么我们就知道是(A1(A2A3))
我们不难发现,加括号的位置其实就是k 的对应序号的矩阵,在写算法时我们就可以用外的数组记录下对应位置的k值。在最后输出时把这个数组按逻辑输出。
最终这个算法的复杂度
(4)代码运行结果实例
四、实验中遇到的问题以及实验体会:
这次实验我们主要采用了动态规划的方法来求解问题,最重要的就是如何建立这个动态规划,通过这次实验,我也对于动态规划的使用条件和设计步骤有了更加深刻的了解。
- 适用条件
1.优化子结构 2.重叠子问题
- 设计步骤
1.找出最优解性质,刻画结构特征 2.递归地定义最优解
3.自底向上的方式计算最优解 4.根据计算最优解时得到的信息,构造最优解
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。
在分析问题的最优子结构性质时,所用的方法具有普遍性:首先假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题最优解更好的解,从而导致矛盾。
利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。最优子结构是问题能用动态规划算法求解的前提。
递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。
动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。
通常不同的子问题个数随问题的大小呈多项式增长。因此用动态规划算法只需要多项式时间,从而获得较高的解题效率。
在这次实验的过程中,如何得知是哪个地方要加括号这个问题我思考了很久,在一遍遍理清楚整个算法的过程后,我有了初步的了解,加括号的位置其实就是k 的对应序号的矩阵,在写算法时我们就可以用另外的数组记录下对应位置的k值。在最后输出时把这个数组按逻辑输出。
完整代码和格式化报告详情见如下链接:
https://download.csdn.net/download/dxxmsl/86951671
https://www.xsblog.site/ <== 我的博客