矩阵连乘问题(内附动态规划算法代码)
矩阵连乘问题
若矩阵A是一个p*q的矩阵,B是一个q*r的矩阵,则C=AB,是一个p*r的矩阵,需进行pqr次数乘计算。
存在{A1,A2,A3}三个矩阵,维数分别为100*5,5*50,50*10。若直接相乘,A1*A2*A3,则需要进行n=100*5*50+100*50*10=25000+50000=75000次数乘计算。如果我们调整运算顺序,A1*(A2*A3),则需要进行n=5*50*10+100*5*10=2500+5000=7500次数乘计算。
由此可见,当进行矩阵连乘运算时,加括号的方式,即计算次序对计算量有很大的影响。
代码展示:
1 #include<iostream> 2 3 using namespace std; 4 /* 5 自底向上的推出矩阵连乘的最优解 6 先从两个矩阵相乘开始,而后三个矩阵相乘,四个......直到推出目标长度的最优解 ,即假设一个矩阵链,初始长度为2,算出所有相邻矩阵相乘的计算次数,而后使其长度为3...4...直到目标长度 7 状态转移方程: 8 m[i][j]=min {m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]} i<=k<j i<j 9 m[i][j]=0 i==j; 10 */ 11 #define LEN 5 //矩阵个数 12 //矩阵连乘函数,找到最优解 13 void MatrixChain(int *p, int m[][LEN + 1], int s[][LEN + 1]) { 14 for (int i = 0; i < LEN + 1; i++) m[i][i] = 0; //初始化,对角线元素置零,即当矩阵链长度为1时(只有一个矩阵)不用乘,为零 15 for (int r = 2; r <= LEN; r++) { //r表示矩阵链的长度,从2开始,两个矩阵相乘,而后3...4...5... 16 for (int i = 1; i <= LEN - r + 1; i++) { //i是矩阵链的首个矩阵,小于矩阵个数减矩阵链长度加一 17 int j = i + r - 1; //j是矩阵链的最后一个元素 18 m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j]; //m[i][j]是子结构,从最左边开始推 19 s[i][j] = i; //标记断开的位置 20 for (int k = i + 1; k < j; k++) { //k是i和j直接的断开点,是在i和j之间的子结构 ,通过k的循环找到最优的解 21 int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; //状态转移方程 22 if (t < m[i][j]) { 23 m[i][j] = t; //更新最优解 24 s[i][j] = k; //更新断开点 25 } 26 } 27 } 28 } 29 } 30 31 //回溯函数,根据s[i][j]数组标记的位置,回溯找到断开的位置 32 void Traceback(int i, int j, int s[][LEN + 1]) { 33 if (i == j) { //当i与j相等 说明回溯到该矩阵的位置了 34 cout << "A" << i; 35 } 36 else { 37 cout << "("; 38 Traceback(i, s[i][j], s); //从尾往头回溯 39 Traceback(s[i][j] + 1, j, s); //从断点往后回溯 40 cout << ")"; 41 } 42 } 43 //输出函数 44 void output(int t[][LEN + 1]) { 45 for (int i = 1; i <= LEN; i++) { 46 for (int j = 1; j <= LEN; j++) { 47 cout << " " << t[i][j] << " "; 48 } 49 cout << endl; 50 } 51 } 52 int main(void) { 53 int p[LEN + 1] = { 6,8,9,3,4,10 }; //矩阵的维度分别是2*3,3*4,4*5,5*6,6*7,LEN+1个数表示LEN个矩阵 54 int m[LEN + 1][LEN + 1] = { 0 }; //记录最优子结构的二维数组 55 int s[LEN + 1][LEN + 1] = { 0 }; //记录最优解对应的括号的位置 56 57 MatrixChain(p, m, s); 58 59 cout << endl; 60 output(m); 61 cout << endl; 62 output(s); 63 cout << endl; 64 cout << "outcome:" <<endl; 65 Traceback(1, LEN, s); 66 cout << endl; 67 68 return 0; 69 }
运行结果:
与备忘录方法的区别:
我们使用的动态规划方法中其实融入了备忘录的一些东西,我们的m和s数组都是用来记录的,所以备忘录方法与我们使用的方法类似,不同在于,我们是自底向上的,而备忘录方法是自顶向下的进行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)