一、实验目的
1.掌握基于使用动态规划的方法求解矩阵连乘问题最优计算次序和最小计算次数问题的原理。
2.掌握求解矩阵链最小数乘次数动态规划函数的设计方法。
3.掌握基于动态规划方法求解矩阵连乘问题算法的具体步骤。
4.掌握运用动态规划的思想和方法设计算法解决和简化其他实际应用问题的能力。
5.体会动态规划的设计思想,通过矩阵连乘实验深刻理解动态规划如何简化计算步骤和减少求解问题时间。
二、实验环境
操作系统:Windows10
实验平台:CodeBlocks
编译器:Gcc
实验语言:C++
三、实验内容
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,(i=1,2 ,…,n-1),考察这n个矩阵的连乘积A1A2...An,矩阵连乘的计算次序可以使用加括号的方法表示,对于n个矩阵连乘的次序,不同的计算次序计算量,即乘法次数是不同的,找出一种最优的计算次序,使得矩阵连乘的次数最小,并给出该次序下,即矩阵连乘的最小次数。
为了使问题描述更清晰,给出例子:现有A1大小为(5*10)的矩阵,A2大小为(10*100)的矩阵,A3大小为(100*2)的矩阵。那么有两种加括号的方法(使用加括号的方法表示计算次序):(A1A2)A3和A1(A2A3),容易得出,前者矩阵连乘次序的计算量为5*10*100+
5*100*2,结果为6000,后者矩阵连乘次序的计算量为10*100*2+5*10*2,结果为2100,可以看出,不同矩阵连乘次序对结果即计算量差别很大。
四、算法描述
文章来源:https://www.toymoban.com/news/detail-415655.html
分析最优解的结构:首先刻画解决矩阵连乘问题的最优解的结构特征,将矩阵连乘积AiAi+1...Aj记为A[i:j],即考察A[1:n]的最优计算次序,考虑在该计算次序在矩阵Ak和Ak+1之间的矩阵链断开,则先计算A[1:k]和A[k+1:n],再将计算结果相乘,可知总计算量为A[1:k]的计算量加上A[k+1:n]的计算量,再加上A[1:k]和A[k+1:n]相乘的计算量,分析可知,计算A[1:n]的最优次序所包含的矩阵子链A[1:k]和A[k+1:n]的次序是最优的。
建立递归关系:设计算A[i:j],所需的最少乘法次数为m[i][j],原问题的最优解即为m[1][n]。当i==j时,A[i:j]=Ai为单一矩阵,即m[i][i]=0,当i<j时m[i][j]的计算可以通过问题的最优子结构求解,即m[i][j]=m[i][k]+m[k+1][j]+pi-1*pk*pj,对于k的位置,有j - 1种可能,为这些可能中使得计算量达到最小的那个位置。可得m[i][j]的递归定义式为:
将对应m[i][j]的断开位置k记为s[i][j],计算出最优值(m[i][j]),即可递归的由s[i][j]构造出最优解。
计算最优值:由递归式可知,计算m[1][n]可以通过递归算法来实现,依据递归式自底向上的进行计算,计算过程中保存已经解决的子问题的答案,避免了大量重复的计算。首先计算出m[i][i] = 0,根据递归式,按矩阵链递增的方式依次计算m[i][i+1], m[i][i+2](矩阵链长度为三),在计算m[i][j]时,只用到已经计算出的m[i][k]和m[k+1][j]。
对所设计算法(主函数为matrixChain)分析可知,该动态规划算法主要的计算量取决于r,i,k的三重循环,三重循环体内的计算量为O(1),三重循环的总次数为O(n^3),该算法的时间复杂度为O(n^3), 空间复杂度为O(n^2)。
五、实验结果
设输入矩阵的个数为6,分别为A1(30*35), A2(35*15), A3(15*5),A4(5*10), A5(10*20), A6(20*25)。通过算法计算得出矩阵连乘的最少次数为15125次,最优计算次序为((A1(A2A3))((A4A5)A6))。
六、实验总结
通过本次实验,我认识到矩阵连乘问题,关键是解决断开点k的位置和最少数乘次数。深刻理解了动态规划算法的几个基本步骤。动态规划的算法设计中,建立递归关系是很关键的一步(确定状态转移方程),是整个动态规划算法的精髓。掌握了递归的思想,就可以完成很多不必要的重复计算。
通过基于动态规划求解矩阵连乘问题的实验,还可以总结使用动态规划求解问题的基本解题步骤,首先要确定子问题,重点分析哪些变量变量是随着问题规模的变小而变小的,哪些变量与问题的规模无关。第二步确定状态,根据找到的子问题来给分割的子问题限定状态,第三步,推导状态转移方程,特别注意状态转移方程需要满足所有的条件,不能有遗漏。第四步,确定边界条件和实现方法,是使用for循环还是其他方法。文章来源地址https://www.toymoban.com/news/detail-415655.html
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 100;
int p[1000];//存储每个矩阵的行数和最后一个的列数
int m[maxn][maxn];//存储最优子结构
int s[maxn][maxn];//存储当前结构的最优断点
void matrixChain(int n,int p[],int m[][maxn],int s[][maxn]){
//对角线先为0
for(int i=1;i<=n;i++)
m[i][i]=0;
for(int r=2;r<=n;r++){//一共n-1个对角线
for(int i=1;i<=n-r+1;i++){//第i行
int j=i+r-1;//在该行的对角线上的点对应的j值
m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//初始化此时在i处取得最优解
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;
}
}
}
}
}
void printBsetOrder(int s[100][100],int i,int j){
if( i == j)
printf("A%d", i);
else{
printf("(");
printBsetOrder(s,i,s[i][j]);
printBsetOrder(s,s[i][j]+1,j);
printf(")");
}
}
int main()
{
int n;
//初始化所需的矩阵
fill(p,p + 1000, 0);
fill(m[0],m[0] + maxn*maxn, 0);
fill(s[0],s[0] + maxn*maxn, 0);
printf("输入矩阵的个数:\n");
scanf("%d", &n);
printf("输入每个矩阵的行数和最后一个矩阵的列数:\n");
for(int i=0;i<=n;i++)
scanf("%d", &p[i]);
matrixChain(n,p,m,s);
printf("矩阵连乘的最少次数为:%d\n", m[1][n]);
printf("最优计算次序:");
printBsetOrder(s,1,n);
return 0;
}
到了这里,关于基于动态规划求解矩阵连乘问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!