矩阵连乘最小相乘次数的思想

矩阵的乘法

矩阵的概念来自线性代数

矩阵乘法:只有当左边矩阵的列数等于右边矩阵的行数
时,它们才可以相乘。

结果为前一个矩阵的行元素×后一个矩阵的列元素

矩阵相乘的最小相乘次数产生的原因

乘积矩阵(相乘的结果)的行数等于左边矩阵的行数乘积
矩阵的行数等于右边矩阵的列数


实例: 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;
}

 完成

 

 

 

 

 

 

posted @ 2022-10-20 23:12  kuailest  阅读(778)  评论(0编辑  收藏  举报