矩阵链相乘问题

矩阵链相乘问题

  问题描述:考虑 n n n个矩阵的乘积: A 1 A 2 … A n A_1A_2…A_n A1A2An,确定最优的乘法顺序(最优括号化方案),使得标量(数值)乘法次数最少。其中, A i A_i Ai p i − 1 ⨯ p i p_{i-1}⨯p_i pi1pi矩阵, i = 1 , 2 , … , n i=1,2,…,n i=1,2,,n

  对于两矩阵元素相乘,结果矩阵的每一个元素,由原先两矩阵的行列对应元素相乘。因此,假设假设 A 1 为 m × n A_1为m\times n A1m×n矩阵, A 2 为 n × k A_2为n\times k A2n×k矩阵, A 1 A 2 = m × k × n A_1A_2=m\times k\times n A1A2=m×k×n,这么理解一共有 m × k m\times k m×k个元素,每个元素进行了 n n n次乘法得到。

  例:假设 A 1 为 10 × 5 A_1为10\times 5 A110×5矩阵, A 2 为 5 × 20 A_2为5\times 20 A25×20矩阵, A 1 为 20 × 10 A_1为20\times 10 A120×10矩阵,则有
   ( A 1 A 2 ) A 3 = 10 × 20 × 5 + 10 × 10 × 20 = 3000 (A_1A_2)A_3=10\times20\times5+10\times10\times20=3000 (A1A2)A3=10×20×5+10×10×20=3000
   A 1 ( A 2 A 3 ) = 5 × 10 × 20 + 10 × 10 × 5 = 1500 A_1(A_2A_3)=5\times10\times20+10\times10\times5=1500 A1(A2A3)=5×10×20+10×10×5=1500

  对于 A 1 A 2 … A n A_1A_2…A_n A1A2An的乘法顺序总数( P ( n ) P(n) P(n)表示乘法顺序总数)可以由以下递归公式确定:
P ( n ) = { 1 n = 1 , 2 ∑ i = 1 n − 1 P ( i ) P ( n − i ) n > 2 P(n)=\begin{cases} 1&n=1,2\\ \sum_{i=1}^{n-1}P(i)P(n-i)&n>2 \end{cases} P(n)={1i=1n1P(i)P(ni)n=1,2n>2
结果称为 C a t a l a n Catalan Catalan数,这个序列的增长速度为 Ω ( 4 n / 3 3 / 2 ) \varOmega(4^n/3^{3/2}) Ω(4n/33/2)因此要想使用暴力搜索是不现实的。

  假设 S ( 1 , n ) S(1,n) S(1,n)所需的最少乘法次数我们有可以得到
S ( 1 , n ) = min ⁡ 1 ≤ i < n ( S ( 1 , i ) + S ( i + 1 , n ) + p 1 p n p i ) S(1,n)=\underset{1\le i<n}{\min} (S(1,i)+S(i+1,n)+p_1p_np_i) S(1,n)=1i<nmin(S(1,i)+S(i+1,n)+p1pnpi)
可知,这个问题符合最优子结构特征,可以由子问题的解组合得到。

  由此我们考虑如何实现这个算法,为了自顶向上的求解这个问题,我们需要知道如何更新 S [ i , j ] S[i,j] S[i,j],可得:
S [ i , j ] = { 0 i = j min ⁡ i < k < j ( S ( i , k ) + S ( k + 1 , j ) + p i p j p k ) i < j S[i,j]=\begin{cases} 0&i=j\\ \underset{i<k<j}{\min} (S(i,k)+S(k+1,j)+p_ip_jp_k)&i<j \end{cases} S[i,j]=0i<k<jmin(S(i,k)+S(k+1,j)+pipjpk)i=ji<j
以上是最少次数的迭代公式,为了构造出最终的最优括号方案,我们用另外的 K [ i , j ] K[i,j] K[i,j]记录 S [ i , j ] S[i,j] S[i,j]时的 k k k值。
K [ i , j ] = { 0 i = j arg ⁡ { k : min ⁡ i < k < j ( S ( i , k ) + S ( k + 1 , j ) + p i p j p k ) } i < j K[i,j]=\begin{cases} 0&i=j\\ \arg\{k:\underset{i<k<j}{\min} (S(i,k)+S(k+1,j)+p_ip_jp_k)\}&i<j \end{cases} K[i,j]=0arg{k:i<k<jmin(S(i,k)+S(k+1,j)+pipjpk)}i=ji<j
代码下载

#include <iostream>
#include <vector>
#include <string>
using namespace std;

//构造最优解 
void ConstructBestAnswer(const vector<vector<int>> &k,int i,int j,string &str){
    if(i>j) return;
    if(i==j) str+=("A"+to_string(i));
    else
    {
        // cout<<i<<","<<j<<endl;
        str+="(";
        ConstructBestAnswer(k,i,k[i][j],str);
        ConstructBestAnswer(k,k[i][j]+1,j,str);
        str+=")";
    }
}

string MatrixChainMultiplication(const vector<int> &MC){
    if(MC.size()<=2) return "输入不正确";
    int n = MC.size()-1;
    //初始化两个二维数组
    vector<vector<int>> S(n,vector<int>(n,0)),K(n,vector<int>(n,0));
    for (int j = 0;j < n; j++)
    {
         
        //更新S[i][j],其中i<=j
        for (int i = j; i >= 0; i--)
        {
            if(i==j) {K[i][j] = i;continue;}
            //设置起始的S[i][j],K[i][j]
            S[i][j] = S[i][i]+S[i+1][j] + MC[i]*MC[i+1]*MC[j+1];
            K[i][j] = i;
            //因为已经计算过从i开始的顺序,所以以下从i+1开始
            for (int k = i+1; k < j; k++)
            {
                int tmp = S[i][k] + S[k+1][j] + MC[i]*MC[j+1]*MC[k+1];
                if(S[i][j]>tmp){
                    S[i][j] = tmp;
                    K[i][j] = k;
                }
            }
            
        }
        
    }

    // for (size_t i = 0; i < n; i++)
    // {
    //     for (size_t j = 0; j < n; j++)
    //     {
    //         cout<<S[i][j]<<",";
    //     }
    //     cout<<endl;
    // }
    // for (size_t i = 0; i < n; i++)
    // {
    //     for (size_t j = 0; j < n; j++)
    //     {
    //         if(i<=j) cout<<K[i][j]<<",";
    //     }
    //     cout<<endl;
    // }
    cout<<"最优解:"<<S[0][n-1]<<endl;
    string ans;
    ConstructBestAnswer(K,0,n-1,ans);
    return ans;
    
}


int main(){
    //算法导论上的案例
    vector<int> input = {30,35,15,5,10,20,25};
    cout<<MatrixChainMultiplication(input)<<endl;
    system("pause");
    return 0;
}

运行截图;

在这里插入图片描述

Reference

《算法导论》第十五章

posted @ 2020-04-03 17:17  马里捡鱼  阅读(22)  评论(0)    收藏  举报