一、概述

以两个矩阵相乘为例,A1*A2,A1和A2为两个矩阵,假设A1的行列数是p*q,A2的行列数是q*r。注意这里由于是A1乘以A2,所以A1的列数要等于A2的行数,否则无法做矩阵乘法,满足上述条件的矩阵,我们称之为“相容”的。那么对于A1*A2而言,我们需要分别执行p*r次对应A1的行元素乘以A2的列元素,根据线性代数知识,不难得出我们一共需要执行p*q*r次乘法。

对于两个矩阵相乘,一旦矩阵的大小确定下来了,那么所需执行的乘法次数就确定下来了。那么对于两个以上的矩阵呢?是不是也是这样呢。实际上,对于多个矩阵相乘,乘法执行的次数与“划分”有关。例如:

以矩阵链<A1,A2,A3>为例,假设三个矩阵的规模分别为10X100,100X5和5X50。

①以((A1*A2)*A3)方式划分,乘法执行次数为:10*100*5+10*5*50=5000+2500=7500次

②以(A1*(A2*A3))方式划分,乘法执行次数为:100*5*50+10*100*50=25000+50000=75000次

我们可以发现,对于同样的矩阵链<A1,A2,A3>相乘而言,不同的划分,乘法次数居然相差10倍。

二、如何获得最佳的矩阵链乘法划分和最少次数

其实这里与“钢管切割”有相似之处,在钢管切割问题中,我们使用一个一维数组来存储最佳收入,另一个一维数组来存储切割的划分处。

而这里,我们也可以将矩阵链看成一根要分割的“钢管”,只是这里记录的两个数组需要是二维的,因为我们需要记录的不仅是从哪里“断开”,还需要记录"每一段"到哪里截止。如下图:

使用一个长度为n+1的一维数组p来记录每个矩阵的规模,其中n为矩阵下标i的范围1~n,例如对于矩阵Ai而言,它的规模应该是p[i-1]到p[i]。由于i是从1到n取值,所以数组p的下标是从0到n。

用于存储最少乘法执行次数和最佳分段方式的结构是两个二维数组m和s,都是从1~n取值。m[i][j]记录矩阵链<Ai,Ai+1,...,Aj>的最少乘法执行次数,而s[i][j]则记录 最优质m[i][j]的分割点k。

需要注意的一点是当i=j时,m[i][j]=m[i][i]=0,因为一个矩阵不需要任何乘法。

假设矩阵链从Ai到Aj,有j-i+1个矩阵,我们从k处分开,将矩阵链分为Ai~Ak和Ak+1到Aj两块,那么我们可以比较容易的给出m[i][j]从k处分隔的公式:

m[i][j]=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];

在一组确定的i和j值的情况下,要使m[i][j]的值最小,我们只要在所有的k取值中,i<=k<j,寻找一个让m[i][j]最小的值即可。

假设L为矩阵链的长度,那么L=j-i+1。当L=1时,只有一个矩阵,不需要计算。那么我们可以从L=2到n进行循环,对每个合理的i和j值的组合,遍历所有k值对应的m[i][j]值,将最小的一个记录下来,存储到m[i][j]中,并将对应的k存储到s[i][j]中,就得到了我们想要的结果。

根据上面的分析,不难给出过程的代码,注意这里也是使用的自底向上的方法,参看“钢管切割”

//Ai矩阵的行列分别是p[i-1]和p[i],1<=i<=n/** 求解最少次数的乘法括号划分方案*/
void Matrix_Chain(int* p, int n, int** m, int** s) {//①将对角线上的值先赋值为0for (int i = 1; i <= n; i++) {m[i][i] = 0;}int l = 0; //l为矩阵链的长度//m[i][j]的第一个参数int i = 0;//m[i][j]的第二个参数int j = 0;int tmp = 0;//②以长度L为划分,L从2开始到nfor (l = 2; l <= n; l++) {//循环第一个参数,因为l的长度至少为2,所以i和j在这个循环里面肯定不相等for (i = 1; i <= n - l + 1; i++) {//因为j-i+1=l,所以j=l+i-1j = i + l - 1;//给m[i][j]赋初值,这里要寻找m[i][j]的最小值,本来应当给m[i][j]赋值一个正无穷,但是这里直接赋一个i=j时候的特值也可以m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j];s[i][j] = i;//对于每个特定的i和j的组合,遍历此时所有的合适k值,k大于等于i小于jfor (int k = i + 1; k < j; k++) { //这里k不能等于j,因为后面要m[k+1][j],不然k+1就比j大了tmp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];if (tmp < m[i][j]) {m[i][j] = tmp;s[i][j] = k;}}}}
}

上面的代码,我们就求得了每种i和j组合对应的最小乘法次数和对应的最佳分割处s[i][j]。

三、输出最优构造划分:

经过运行上面的代码,我们就准备好了s[i][j],其中包含最佳分割信息。我们可以使用一种类似于中序遍历的方法来输出划分方式,比如对<A1,A2,A3,A4,A5>和他们对应的下标数组p而言。

void print_optimal_parens(int** s, int i, int j) {if (i == j) {cout << "A" << i;} else {cout << "(";print_optimal_parens(s, i, s[i][j]);print_optimal_parens(s, s[i][j] + 1, j);cout << ")";}
}

比如对于数组p={5,6,2,9,7,6}和<A1,A2,A3,A4,A5>经过上面两段代码的调用,输出划分结果:

((A1A2)((A3A4)A5))

最少乘法次数为:330次

备注:

动态规划是一种将问题实例分解为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。

备忘录法是动态规划方法的变形。与动态规划算法不同的是,备忘录方法的递归方式是自顶向下的,而动态规划算法则是自底向上的。

转载链接:

https://blog.csdn.net/cyp331203/article/details/42965237

矩阵链乘法问题 (算法)相关推荐

  1. 矩阵链乘法 自顶向下 自底向上 Python 实现 算法导论

    算法导论 矩阵链乘法 自顶向下 自底向上 Python 实现 带备忘的自顶向下实现方式 def Memoized_Matrix_chain(p):n = len(p)m = [[0 for i in ...

  2. python矩阵乘法算法_Python算法|矩阵链乘法

    概述 矩阵乘法是一个满足结合律的运算.显然,对于矩阵A.B.C来说,(AB)C 与 A(BC) 是等价的,我们可以根据自己的心情选择任意的运算顺序,总之,结果都是一样的. 糟糕的是,对计算机来说可不是 ...

  3. 【算法分析与设计】矩阵链乘法最优顺序问题

    矩阵链乘法 矩阵连乘,选择不同的乘法顺序,效率可能千差万别. 高效的实现算法应该用动态规划来设计,具体的讲解可以看这里. 编程实现 public class Main {private static ...

  4. 【动态规划】矩阵链乘法

    矩阵链乘法    求解矩阵链相乘问题时动态规划算法的另一个例子.给定一个n个矩阵的序列(矩阵链)<A1,A2,...,An>,我们希望计算它们的乘积  A1A2...An    为了计算表 ...

  5. C++matrix chain multiplication矩阵链乘法算法的实现(附完整源码)

    C++lmatrix chain multiplication矩阵链乘法算法的实现 C++matrix chain multiplication矩阵链乘法算法的实现的完整源码(定义,实现,main函数 ...

  6. 15.2 矩阵链乘法

    1.代码 public class MatrixChainMultiplication {public static void main(String[] args) { // 在该代码中,我们首先创 ...

  7. 动态规划典型题之——矩阵链乘法

    动态规划是算法分析与设计中一种重要的算法.其核心思想是将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法. 文章目录 一.矩阵链相乘问题 二.求解 1.构建备忘录 2.回溯 结果展示 一.矩 ...

  8. c语言备忘录算法矩阵链乘,矩阵链乘法(备忘录法)

    /* * @fileMemoMatrixchain.cpp * @briefa solution of martrix chain with memorized way. * @author/Univ ...

  9. 矩阵相乘的strassen算法_矩阵乘法的Strassen算法+动态规划算法(矩阵链相乘和硬币问题)...

    矩阵乘法的Strassen 这个算法就是在矩阵乘法中采用分治法,能够有效的提高算法的效率. 先来看看咱们在高等代数中学的普通矩阵的乘法 两个矩阵相乘 上边这种普通求解方法的复杂度为: O(n3) 也称 ...

最新文章

  1. 2021科大讯飞-车辆贷款违约预测赛事 Top1方案!
  2. VS2015下使用websocketpp和asio构建websock服务器
  3. 【性能优化实战】java嵌入式开发pos
  4. git版本分支和分支、分支和主分支切换
  5. java实现随机验证码的图片
  6. mysql aes java解密_加密/解密的Java函數,如Mysql的AES_ENCRYPT和AES_DECRYPT
  7. 2M线路保护实现与应用
  8. php中如何将验证码放入页面,如何在php中生成验证码图片
  9. 重构《一》-- 提取方法
  10. fastjson和json-lib的区别
  11. JMeter中如何实现跨线程组关联
  12. BI工具的主要功能都有哪些
  13. 【理论篇】是时候彻底弄懂BERT模型了(建议收藏)
  14. c语言库函数手册pdf百度云,C语言库函数手册.pdf
  15. mgr.dll病毒手工清除方法!
  16. 关于mysql的时区(下):如何设置mysql的时区
  17. 正则表达式之前瞻后顾
  18. 程序员月薪5W却发出哀叹:家庭枷锁太重,生活如同围城
  19. 小时候 觉得爸爸就是天 无所不能~
  20. 防止PCB会过期,以及过期后的处理办法

热门文章

  1. Linux企业级服务之实现DNS子域服务器
  2. 线段,射线,直线的关系
  3. 做我女朋友好吗小程序c语言,抖音做我女朋友好吗程序代码是什么 抖音做我女朋友程序怎么弄-站长资讯中心...
  4. oracle系统表空间和自定义表空间
  5. 硬核 | Redis 布隆(Bloom Filter)过滤器原理与实战
  6. 美团王兴要向口碑饿了么学啥?
  7. Flask 引入swagger
  8. 实验6 图及其应用——图的遍历
  9. `Algorithm-Solution` `AcWing` 378. 骑士放置
  10. PAT日志 1011