博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【算法概论】动态规划:矩阵连乘问题
阅读量:4177 次
发布时间:2019-05-26

本文共 2756 字,大约阅读时间需要 9 分钟。

矩阵连乘问题(Matrix Multiplication Order Problem / Chain Matrix Multiplication)

【文中所有标蓝字体为下标】

问题描述:

       有 n 个矩阵进行连乘,已知各个矩阵的行列数,问:如何在算式的中间插入合适的括弧,使得乘法的进行次数最少?

问题解释:

       一个 m * n 的矩阵与一个 n * p 的矩阵相乘,越需要进行 m * n * p 次乘法。矩阵的乘法虽不满足交换律,但满足结合律?。我们可以通过对矩阵进行合适的结合,使得进行的乘法次数最少。

❗算法分析❗:

       用动态规划解决问题分为四个步骤?:

       ① 定义子问题,并检查是否满足最优子结构;

       ② 确定求解子问题的次序;

       ③ 定义决策方程和状态转移方程;

       ④ 回溯最优解?

       首先来定义子问题:在本题中,要求矩阵连乘运算进行的乘法次数最少。假设矩阵 A1 * A2 * A3 * … * An-2 * An-1 * An 的 最优解为 ( A1 * A2 * A3 * A4 * … * Ak ) * (Ak+1 * Ak+2 * … * An-1 * An) ,那么从 A1 到 Ak 呢?从 Ak+1 到 An 呢?它们一定是进行这个问题的最优解(๑•̀ㅂ•́)و✧!这样下来,我们容易想到,子问题 应该定义为: C(i, j) = 计算 Ai * Ai+1 * … * Aj-1 * Aj 的最小代价。最小子问题 为 i = j时,此时做乘法的矩阵。

       所以,状态转移方程为 :C(i, j) = min(C(i, k) + C(k+1, j) + mi-1 * mk * mj) ?

       子问题的次序:bottom-up,自顶向上。

       接下来分析另外一个问题,请看下图:

       注意上下图中,m[1, n] 指向的位置,别收到旁边一竖列 n → 1 的影响,差点晕了?

       注意!?上面的这个矩形图中标灰色的区域,意味着我们可以不用计算?‍。

       上述的矩形图有什么意义呢?m[1, n] gives the optimal solution to the problem → m[1, n]给出了问题的最优解 —— 进行乘法运算的次数。灰色区域的 i 值 大于 j 值,是没有意义的! 

回溯最优解:

       我们可以用一个二维数组s,s[i][j]记录了切分位置k,同理,下半部分也不要。

下面给出代码✍:

#include 
#include
#include
using namespace std;//int min(int, int);/*回溯最优解 s[i][i] 存储 从 i 到 j 的 最优切分位置 */string result(vector
> &s, int k1, int k2);int main(){ int num; cout << "请输入矩阵的个数:" << endl; cin >> num; vector
> matrix(num, vector
(2, 0)); //num行两列的二维向量,用于存储各个矩阵的行列数 cout << "请输入各个矩阵的行列数:" << endl; for (int i = 0; i < num; ++i) { for (int j = 0; j < 2; ++j) { cin >> matrix[i][j]; } } vector
> m(num, vector
(num, INT_MAX)); //m[i][j]记录问题的最优解, 各个元素初始化为INT_MAX!!! vector
> s(num, vector
(num, -1)); //s[i][j]回溯问题的最优解 → 记录切分位置k //当 i = j 时, m[i][j] = 0 for (int i = 0; i < num; ++i) { m[i][i] = 0; } //自底向上,对角线方向进行 for (int i = 1; i < num; ++i) //控制沿对角线遍历的次数 √ { for (int j = 0; j < num - i; ++j) //控制每条对角线上元素的个数 √ { int temp = i + j; for (int k = j; k < temp; ++k) { //m[j][temp] = min(m[j][k] + m[k + 1][temp] + (matrix[j][0] * matrix[k][1] * matrix[temp][1]), m[j][temp]); if (m[j][k] + m[k + 1][temp] + (matrix[j][0] * matrix[k][1] * matrix[temp][1]) < m[j][temp]) { m[j][temp] = m[j][k] + m[k + 1][temp] + (matrix[j][0] * matrix[k][1] * matrix[temp][1]); s[j][temp] = k; } } } } //输出最优解 cout << "最小乘法次数:" << endl; cout << m[0][num-1] << endl; //回溯最优解 cout << "分割方法:" << endl; /*for (int i = 0; i < num; ++i) { for (int j = 0; j < num; ++j) { cout << i << ' ' << j << ' ' << s[i][j] << endl; } } cout << endl; cout << endl;*/ cout << result(s, 0, num - 1) << endl; cout << endl; return 0;}//回溯最优解string result(vector
> &s, int k1, int k2){ if (k1 == k2) { return string(1, 'A' + k1); } int split = s[k1][k2]; return " ( " + result(s, k1, split) + " * " + result(s, split + 1, k2) + " ) ";}//int min(int x, int y)//{// return x < y ? x : y;//}

 

转载地址:http://mttai.baihongyu.com/

你可能感兴趣的文章
(转载)linux命令之二十七gzip命令
查看>>
(转载)linux命令之二十八df 命令
查看>>
(转载)linux命令之二十九du 命令
查看>>
生活中我们需要学习的各种“定律”
查看>>
CentOS在VirtualBox下安装没有图形界面的解决方法
查看>>
openstack_juno_allinone_ubuntu的脚本文件
查看>>
Qt浅谈之四十一QLineEdit的新样式和补全历史记录
查看>>
Qt浅谈之四十Centos下Qt结合v4l2实现的视频显示
查看>>
centos下动态gif图和视频的录制
查看>>
Qt浅谈之右下角浮出界面
查看>>
Qt浅谈之四十二钟表摆动显示百分比
查看>>
Qt浅谈之窗体缩放(仅增加测试代码)
查看>>
Qt浅谈之四十三Linux下有系统托盘再运行弹出已运行的实例
查看>>
Qt浅谈之四十四动态显示日志(QGraphicsItem)
查看>>
Qt浅谈之四十五QSplitter实现自由伸缩滑动窗口
查看>>
QT5生成的exe自动拷贝依赖的dll并打包的方法
查看>>
Qt浅谈之四十六QemuQuestAgent的应用
查看>>
Qt::ConnectionType 解析
查看>>
windows下exe程序获得管理员权限
查看>>
windows下VisualStudio和QtCreator搭建Qt开发环境
查看>>