矩阵连乘最小相乘次数的思想
矩阵的乘法
矩阵的概念来自线性代数
矩阵乘法:只有当左边矩阵的列数等于右边矩阵的行数
时,它们才可以相乘。
结果为前一个矩阵的行元素×后一个矩阵的列元素
矩阵相乘的最小相乘次数产生的原因
乘积矩阵(相乘的结果)的行数等于左边矩阵的行数乘积
矩阵的行数等于右边矩阵的列数
实例: 2x 3的矩阵乘以3x 4的矩阵得2 x 4的矩阵
矩阵连乘,简单来说是多个矩阵进行相乘。
但不同的运算次序,其计算量也大概率不同。
如:假设A为10 * 100的矩阵,B为100* 5的矩阵,C为5 * 50的矩阵,
那么矩阵连乘有两种情况:
1、(A* B)*C-共要乘10*100*5+10*5*50 = 7500次
2、A*(B*C)一共要乘100*5*50+10*100 * 50 = 75000次
同样的例子:
第一种(A* B)*C
{( 2:3 )*( 3:2 )}*(2:4 ) -> 2x3x2 = 12 (2:2)*(2:4 ) -> 2x2x4=16. 12 + 16 = 28
第二种
( 2:3 )*{( 3:2 )*(2:4 )} -> 3x2x4 = 24 (2:3)*(3:4 ) -> 2x3x4=24. 24 + 24 = 48
动态规划思路
求最小的矩阵连乘,相当于,在每一个子问题中找到最小的连乘数
适用动态规划的思想
开始时看
当只有两个矩阵时
很显然,只有一种结果
就是从中间进行断开
三个矩阵的时候
我们应该分开计算前两个和后两个的解
之后进行比较 ,获得子问题的最优结构
计算前两个和后两个
获得两个子问题解后后与剩下的计算相比较就能得到: 三个矩阵情况下的最优解
四个矩阵的时候
很显然我们应该和三个的情况一样
从下往上进行计算最优解
1:计算第三层中,每两个相邻向量的解(因为相邻向量的前一个行数和后一个列数相同才能计算);
2:计算第二层中的,三个相邻组合的最优解,同上文;
设k为隔板,k = 2 时,表示(A1*A2)* A3 为最优解;
k 的取值范围是 (i , j-)
在第二层的对比中,我们重复利用了第三层的计算值
因此我们可以将已经计算过的值放入表中,减少重复的计算
3:计算第一层,很显然就是将第二层的两个最优解与剩下的相乘即可
计算这六个矩阵
我们可以发现我们是将这些矩阵分开计算
先是两两一组,之后是三个一组,以此类推到五个一组,最后得出6个一组的最小值
将每一次的计算结构放入表中减少重复的计算
可得数学方程如下
代码思路
1:我们创建一个 N + 1阶的二维矩阵,因为方便我们按 1 到 N 进行计算;
2:创造一个数组M[ ][ ]存入每个矩阵之间相乘的值,同时创造一个数组S [ ][ ] 存入每个组合中最小乘数时需要断开的地方K值
m中存储的值是让算出来的最小乘法次数,比如m[1][5]就是A1A2A3A4A5的最小乘法次数
s中存储的是获取最小乘法次数时的断链点,.s[1][5]对应的就是如何拆分A1A2A3A4A5,
比如S[1][5]=3可表示,(A1A2A3)(A4A5)。 当然内部断链还会继续划分A1A2A3
3:计算数组M中的值,先得出0,因为自己一个的最小就是0,之后对应计算x , y , z,对应这些组合中矩阵的最小值
4.之后进行计算放入所有的最优值
5,反向追踪找最佳断点,递归出来就可以了
代码和解析
#include <iostream> using namespace std; #define N 7 //N为7 , 实际表示有6个矩阵 /* * 计算出需要的对应矩阵 */ int MatrixChain(int *p,int n,int m[][N],int s[][N]){ for(int i = 1 ; i <=n ;i++){ m[i][i] = 0; } for(int size = 2 ; size <=n ; size++){ //矩阵链的长度,从2开始,不断计算各种长度的最优解 for(int i = 1;i <= n-size+1; i++){ //i是矩阵开始计算的起始点,应该是【1,2,3,4,5】【1,2,3,4】【1,2,3】这样出现 int j = i + (size -1);//矩阵链结束计算的地方,应该是开始的地方加上本次循环中链的长度减一 m[i][j] = m[i][i] + m[i+1][j] + p[i-1]*p[i]*p[j];//前面的最优值加上后面的最优值加上之后矩阵的计算量 s[i][j] = i;//将得出的断开位置,放入矩阵S中 for(int k = i+1;k<j;k++){//将断点由i+1的地方移动到j-1为止,如(1|2,3,4)->(1,2|3,4)->(1,2,3|4) int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];//再次使用这个公式 if(t < m[i][j]){ //使得最小的对应值和分割点能存入M和S矩阵 m[i][j] = t; s[i][j] = k; } } } } return 0; } /* * 根据输入的,限定需要获取的矩阵链的始末位置,.s存储断链点 */ void Traceback(int i,int j,int s[][N]){//由值还原解 if(i == j){//回归条件 cout << "A" << i; }else{ //按照最佳断点一分为二,进行递归 cout<<"("; Traceback(i,s[i][j],s);//从i到i和j中最优断开点回归,下同 Traceback(s[i][j]+1,j,s); cout<<")"; } } int main() { int p[N] = {30,35,15,5,10,20,25};//相当于{(30,35),(35,15),(15,5),(5,10),(10,20),(20,25)}这六个矩阵 int m[N][N],s[N][N]; MatrixChain(p, N - 1, m, s); Traceback(1, 6, s); return 0; }
完成