您的位置:首页 > 编程语言 > C语言/C++

矩阵连乘 动态规划(自顶向下递归、自底向上非递归)(C++)

2020-06-01 05:12 746 查看

文章目录

  • 二、算法实现
  • 一、关于矩阵连乘

    1. 问题描述:

    • 给定n个矩阵:A1,A2,…,An,其中Ai与Ai+1是可乘的(i=1,2…,n-1)。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
    • 要求:输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。

    2. 问题分析:

    • 引(++矩阵乘法++): 定义:设A为 m×p 的矩阵,B为 p×n 的矩阵,那么称矩阵A与B的乘积C为 m×n 的矩阵,记作C=AB,其中矩阵C中的第i行第j列元素可以表示为:
      (AB)ij=∑k=1paikbkj=ai1b1j+ai2b2j+⋅⋅⋅+aipbpj (AB)_{ij}=\sum_{k=1}^p a_{ik}b_{kj}=a_{i1}b_{1j}+a_{i2}b_{2j}+···+a_{ip}b_{pj} (AB)ij​=k=1∑p​aik​bkj​=ai1​b1j​+ai2​b2j​+⋅⋅⋅+aip​bpj​
      例:A=[a1,1a1,2a1,3a2,1a2,2a2,3] B=[b1,1b1,2b2,1b2,2b3,1b3,2] C=AB=[c1,1c1,2c1,3c2,1c2,2c2,3] 例:A=\begin{bmatrix} a_{1,1} & a_{1,2} & a_{1,3} \\ a_{2,1} & a_{2,2} & a_{2,3} \end{bmatrix}  B=\begin{bmatrix} b_{1,1} & b_{1,2} \\ b_{2,1} & b_{2,2} \\ b_{3,1} & b_{3,2} \end{bmatrix}  C=AB=\begin{bmatrix} c_{1,1} & c_{1,2} & c_{1,3} \\ c_{2,1} & c_{2,2} & c_{2,3} \end{bmatrix} 例:A=[a1,1​a2,1​​a1,2​a2,2​​a1,3​a2,3​​] B=⎣⎡​b1,1​b2,1​b3,1​​b1,2​b2,2​b3,2​​⎦⎤​ C=AB=[c1,1​c2,1​​c1,2​c2,2​​c1,3​c2,3​​]
    • 数乘次数
      (1)如果A是m×p的矩阵,B是p×n的矩阵,那么乘积C是m×n的矩阵。
      (2)矩阵C共有m×n个元素,每个元素为A的一行与B的一列对应元素相乘再相加(此过程有p次数乘);
      (3)故共计算了m×n×p次数乘。
    • 由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。 完全加括号的矩阵连乘积可递归地定义为:
      (1)单个矩阵是完全加括号的;
      (2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)。
    • 例:矩阵连乘 A1A2A3A4 有5种不同的完全加括号方式:
      (A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)

    ·

    二、算法实现

    1. 自顶向下 递归 的动态规划

    /*矩阵连乘的自顶向下递归的动态规划算法*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    const int INF = 0x3f3f3f3f; // 定义一个“无限大”的值,让每个求得的值与它比较
    int n;      // 矩阵个数
    int* p;     // 记录矩阵的维数
    int** m;    // 记录子链的数乘次数,初始值为0
    int** s;    // 记录子链的断开位置k
    
    int MatrixChain_top_down(int i, int j);
    void DataInput();
    void Traceback(int i, int j);
    
    int main()
    {
    cout << "矩阵连乘的自顶向下递归的动态规划算法:\n\n";
    
    DataInput();
    cout << "最优解为:" << MatrixChain_top_down(1, n) << endl;
    Traceback(1, n);
    
    cout << endl;
    return 0;
    }
    
    int MatrixChain_top_down(int i, int j)
    {
    if (i == j)
    return 0;
    if (m[i][j] != -1)
    return m[i][j];
    int low = INF;
    for (int k = i;k < j;k++) {
    int t = MatrixChain_top_down(i, k) + MatrixChain_top_down(k + 1, j) + p[i - 1] * p[k] * p[j];
    if (t < low) {
    low = t;
    s[i][j] = k;
    }
    m[i][j] = low;
    }
    return low;
    }
    
    void DataInput()
    {
    cout << "请输入矩阵个数:";
    cin >> n;
    
    p = new int[n + 1];     // 矩阵的维数
    m = new int* [n + 1];   // 记录子链的数乘次数,初始值为-1
    for (int i = 0;i < n + 1;i++) {
    m[i] = new int[n + 1];
    for (int j = 0;j < n + 1;j++)
    m[i][j] = -1;
    }
    s = new int* [n + 1];   // 记录子链的断开位置k
    for (int i = 0;i < n + 1;i++)
    s[i] = new int[n + 1];
    
    cout << "请输入矩阵各维数值:\n";
    for (int i = 0;i < n + 1;i++)
    cin >> p[i];
    
    }
    
    void Traceback(int i, int j)
    {
    if (i == j) {
    cout << "A" << i;
    return;
    }
    cout << "(";
    Traceback(i, s[i][j]);
    Traceback(s[i][j] + 1, j);
    cout << ")";
    }

    2. 自底向上 非递归 的动态规划

    /*矩阵连乘的自底向上非递归的动态规划算法*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    int n;      // 矩阵个数
    int* p;     // 记录矩阵的维数
    int** m;    // 记录子链的数乘次数,初始值为0
    int** s;    // 记录子链的断开位置k
    
    void MatrixChain_bottom_up(int n, int* p, int** m, int** s);
    void DataInput();
    void Traceback(int** s, int i, int j);
    
    int main()
    {
    cout << "矩阵连乘的自底向上非递归的动态规划算法:\n\n";
    
    DataInput();                        // 获取用户输入
    MatrixChain_bottom_up(n, p, m, s);  // 自底向上计算数乘次数
    
    cout << "最优解为:" << m[1][n] << endl;
    Traceback(s, 1, n);
    
    // 删除指针
    delete[] p;
    for (int i = 0;i < n + 1;i++) {
    delete[] m[i];
    delete[] s[i];
    }
    delete[] m;
    delete[] s;
    
    cout << endl;
    return 0;
    }
    
    void MatrixChain_bottom_up(int n, int* p, int** m, int** s)
    {
    for (int r = 2;r <= n;r++) {    // r代表子链长度
    // i从1开始,一直到n-r+1,
    // i表示每一个区间长度为r的区间左端点,
    // i=n-r+1是最后一左个端点,此时区间表示为[n-r+1,n],长度为r
    for (int i = 1;i <= n - r + 1;i++) {
    // j表示区间右端点
    int j = i + r - 1;
    // 先求一个值,然后再让其它值与这个值比较
    m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j];
    s[i][j] = i;
    // 计算所有划分,看哪个小于先前的划分
    for (int k = i + 1;k < j;k++) {
    int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
    if (t < m[i][j]) {
    m[i][j] = t;
    s[i][j] = k;
    }
    }
    }
    }
    }
    
    void DataInput()
    {
    cout << "请输入矩阵个数:";
    cin >> n;
    
    p = new int[n + 1];     // 矩阵的维数
    m = new int* [n + 1];   // 记录子链的数乘次数,初始值为0
    for (int i = 0;i < n + 1;i++) {
    m[i] = new int[n + 1];
    for (int j = 0;j < n + 1;j++)
    m[i][j] = 0;
    }
    s = new int* [n + 1];   // 记录子链的断开位置k
    for (int i = 0;i < n + 1;i++)
    s[i] = new int[n + 1];
    
    cout << "请输入矩阵各维数值:\n";
    for (int i = 0;i < n + 1;i++)
    cin >> p[i];
    
    }
    
    void Traceback(int** s, int i, int j)
    {
    if (i == j) {
    cout << "A" << i;
    return;
    }
    cout << "(";
    Traceback(s, i, s[i][j]);
    Traceback(s, s[i][j] + 1, j);
    cout << ")";
    }

    3. 运行结果展示

    ·

    *其它一些常见算法请参阅此链接~

    最后,非常欢迎大家来讨论指正哦!

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: