矩阵连乘--动态规划

动态规划算法解矩阵连乘问题

1 用动态规划算法解矩阵连乘问题

(1)问题的描述

给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要算出这n个矩阵的连乘积A1A2…An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。完全加括号的矩阵连乘积可递归地定义为: 

(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)。每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。若A是一个p×q矩阵,B是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。 

(3)为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察3个矩阵{A1,A2,A3}连乘的情况。设这三个矩阵的维数分别为10×100,100×5,5×50。加括号的方式只有两种:((A1A2)A3),(A1(A2A3)),第一种方式需要的数乘次数为10×100×5+10×5×50=7500,第二种方式需要的数乘次数为100×5×50+10×100×50=75000。第二种加括号方式的计算量时第一种方式计算量的10倍。由此可见,在计算矩阵连乘积时,加括号方式,即计算次序对计算量有很大的影响。于是,自然提出矩阵连乘积的最优计算次序问题,即对于给定的相继n个矩阵{A1,A2,…,An}(其中矩阵Ai的维数为pi-1×pi,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…An的计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。 正如课堂中所分析,穷举法的计算量太大,它不是一个有效的算法,本实验采用动态规划算法解矩阵连乘积的最优计算次序问题。

 

2 算法设计思想

动态规划算法的基本思想是将待求解问题分成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,动态规划法经分解得到的子问题往往不是相互独立的,前一子问题的解为后一子问题的解提供有用的信息,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。 

本实验的算法思路是: 

    1)计算最优值算法MatrixChain():建立两张表(即程序中的**m和**s,利用二维指针存放),一张表存储矩阵相乘的最小运算量,主对角线上的值为0,依次求2个矩阵、3个矩阵…、直到n个矩阵相乘的最小运算量,其中每次矩阵相乘的最小运算量都在上一次矩阵相乘的最小运算量的基础上求得,最后一次求得的值即为n个矩阵相乘的最小运算量;另一张表存储最优断开位置。 

     2)输出矩阵结合方式s,自己加括号,构造出最优加括号方式。当然也可以编程输出,这部分不作强制要求。

 

3 程序设计

   //测试数据可以设为六个矩阵分别为

   //A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]

   //p用来记录矩阵的行列,则p[1-7]={30,35,15,5,10,20,25}

   //m[i][j]用来记录第i个矩阵乘至第j个矩阵的最优解(即最小乘积次数)

   //s[][]用来记录从哪里断开,才可得到该最优解

 

 

计算示例: 

 


 

注意上图(a)中的计算次序!

(1)理解算法思想,读懂程序

(2)给程序注上详细解释

(3)通过矩阵s给出最优乘积次序

 

程序及详解,最优乘积输出附此

1 代码:

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;


void  Matrixchain(int * p, int n, int m[][100], int s[][100])
{
    int i,j,k,r,t;
    for(i=1; i<=n; i++) m[i][i]=0;                  
    for(r=2; r<=n; r++)
        for(i=1; i<=n-r+1; i++)
        {
            j=i+r-1;
            m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];     
            s[i][j]=i;                              
            for(k=i+1; k<j; k++)
            {
                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 Traceback(int i, int j, int s[][100])
{
    if(i==j) {
cout<<"A"<<i;
return;
}
   cout<<"(";
    Traceback(i,s[i][j],s);
   cout<<",";
    Traceback(s[i][j]+1, j, s);
   cout<<")";
}
    
int main()
{
    int size[100], total, i=0, m[100][100], s[100][100];
    cout<<"请输入矩阵的个数:"<<endl;
    cin>>total;
    cout<<"请分别输入每一个矩阵的行列数:"<<endl;
    while(i<=total)
        cin>>size[i++];
    Matrixchain(size, total, m, s);
cout<<"该"<<total<<"个矩阵的最优计算次序:"<<endl;
    Traceback(1,total, s);
    cout<<endl;
    return 0;
}

输出结果

 

2 算法时间复杂度与空间复杂度分析

时间复杂度分析:

首先分析该算法有三大循环块,第一块初始化m,第二块初始化s,第三块是对出m和s的值,第四块是输出表(b),第五块是输出表(c)。

分别求出时间复杂度,第一块为O(6),第二块为O(6),第一块最大为O(5*5*5)=O(125)第二块为O(6*6)=O(36)第三块为O(6*6)=O(36)

因此时间复杂度T=O(6)+O(6)+O(125)+0(36)+O(36)=O(125)

空间复杂度分析:

因为此算法时间复杂度为O(5^3),因此空间复杂度为O(1)。



你可能感兴趣的:(矩阵连乘--动态规划)