矩阵连乘问题

若矩阵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次数乘计算。

由此可见,当进行矩阵连乘运算时,加括号的方式,即计算次序对计算量有很大的影响。

矩阵连乘python原理与思路 矩阵连乘问题代码_最优解

矩阵连乘python原理与思路 矩阵连乘问题代码_最优解_02

矩阵连乘python原理与思路 矩阵连乘问题代码_矩阵连乘python原理与思路_03

 

矩阵连乘python原理与思路 矩阵连乘问题代码_矩阵连乘python原理与思路_04

 

代码展示:

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 }

 运行结果:

矩阵连乘python原理与思路 矩阵连乘问题代码_矩阵相乘_05

 

与备忘录方法的区别:

我们使用的动态规划方法中其实融入了备忘录的一些东西,我们的m和s数组都是用来记录的,所以备忘录方法与我们使用的方法类似,不同在于,我们是自底向上的,而备忘录方法是自顶向下的进行。