动态规划(一)矩阵连乘问题

动态规划真的是一个有点难理解的算法!!!头大!!! 一个矩阵连乘就研究了好久!!!

正题正题:

问题:给定n个矩阵{A1,A2,A3,A4......An},其中Ai与A(i+1)是可乘的,i=1,2,…,n-1.求这个矩阵集合的最少计算量。

思路:首先,A1与A2能相乘必须满足A1的列数与A2的行数相等,举个例子来说,一个5行3列的矩阵A1我把它记作A1(5,3),一个3行4列的矩阵A2(3,4),它们满足条件,可以相乘,计算量为5*3*4=120.(关于计算量为啥是这个,请参考高等数学之矩阵的乘法)

          那么给定一个矩阵序列 A1(10.100) A2(100,1000) A3(1000,1) 如果按照(A1A2)A3的次序来乘,计算量为10*100*1000+10*1000*1=10010000;而采用A1(A2A3)这个次序来乘,计算量为100*1000*1+10*100*1 = 101000.计算量缩小了很多很多,所以我们要找到一种计算次序,使得n个矩阵连乘的计算量最小。

         先附上动态规划的四个基本步骤:①找出最优解的性质,并刻画其结构特征。②递归地定义最优值。③以自底向上地方式计算出最优解。④根据计算最优值时得到的信息,构造最优解。

         拿到这个问题,我们要求A1到An的最小计算量,我们就先用一个二维数组来表示计算量,M[ i ][ j ]。为了区别矩阵的行和列,中括号里面的时第 i 到第 j 的矩阵计算量!!!min{ M[1][n] },因为这里的M[ 1,n ]是个不确定的值,只有通过循环比较才能找到最优解,所以min{ M[1 ][ n] }就是问题的解:A1乘到An的最小计算量。

        然后我们定义一个kk表示 ij之间的一个断点,也就是说将原来的那个矩阵序列用这个断点位置分为两个矩阵序列,那么计算量就为分开的两个矩阵序列的子计算量的和。而最优计算量就是k的其中一个位置,等会会用一个循环将k在i,j之间所有位置都循环一遍,找到最优的那个位置。

        接下来我们定义个一维数组p,p里面存放的时矩阵的行和列的下标,例如A1(3,4)A2(4,5)A3(5,2) 那么p[0] = 3 ,因为第一个矩阵的列和第二个矩阵的行时相等的,那么只需记一次,即p[1] = 4,p[2] = 5,p[3] = 2,接下来的p[ i ]就是第i个矩阵的列数.

       此时可以得出一个递归关系最小计算量M[ i ][ j ] = min{ m[ i ][ k ] + m[ k+1][ j ] + p[ i-1 ]*p[ i ]*p[ j ] } (i < j)  当 i = j 时,M[ i ][ j ]就为一个数组,计算量就等于0;注:怎么理解这个表达式, 就是一个k将原矩阵序列分为了两段,总计算量就等于第一段的计算量M[ i ][ k ]加上第二段的计算量M[ k+1 ][ j ],再加上第一段最后所得的那个矩阵与第二段最后所得的那个矩阵的计算量p[ i-1 ]*p[ i ]*p[ j ]。至于为什么是这个   p[ i-1 ]*p[ i ]*p[ j ] 你可以举三个矩阵的例子来帮助理解以下,这里就不多做举例了。

       做好了以上的那些铺垫,我们就要来设计程序的算法了。回到问题,我们直接要求M[ 1 ][ n ]的最小计算量 肯定是不现实的,因为k在1到n中间分了位置,你也无法直接得出M[ 1 ][ k ]与M [ k+1 ][ n ]的计算量,所以我们要去找那个最底层的计算量,从底层开始算到最后的外层,就像造房子一样,要一层一层的往上搭。而这里的最底层(因为所有一个矩阵相乘的情况计算量都为0,所以所有的M[ i ][ i ] =0),就是只有两个矩阵相乘的计算量,例如A1A2 , A2A3 ,A3A4.....,先将他们的计算量存放到M[ i ][ j ]这个二维数组里面,例如A1A2就存在M[1][2]里面,A3A4就存在M[3][4]里面,然后通过两层循环,得出三个矩阵相乘的情况,如M[ 1 ][ 3 ]等,四个矩阵相乘的情况,如M[ 1 ][ 4 ]等。这里就可以得出一个M[ i ][ j ]的最初表达式 M[ i ][ j ] = M[ i+1 ][ j ] + p[ i -1]*p[ i ]*p[ j ]  . 注:怎么理解这个表达式 ,就是要算M[ i ][ j ] ,直接将这个矩阵分成两部分,Ai与M[ i+1 ][ j ],因为一个矩阵相乘的计算量为0,所以M[ i ][ j ] = M[ i+1 ][ j ] + p[ i -1]*p[ i ]*p[ j ] 。 最后就是通过循环比较查找k的最优位置,从而得出最小计算量M[ i ][ j ].

附:算法代码:

    MatrixChain(int *p ,int n,int **m)
{
    for(int i = 1;i <= n;i ++) m[ i ][ i ] =0;    //将所有一个矩阵相乘的情况都赋初值0 
    for( r =2; r <= n;){
        for(int i; i<= n-r+1;i++){
                int j = i + r - 1;
                m[i][j] = m[i+1][j]+p[i-1]*p[i]*p[j];
                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;}
                                                        }
                                               }
                                            }    
    
}
                                 

最后那个代码没有经过测试,可能会有点小问题,不过具体的思路我已经讲的很详细了,要是大神们有啥意见和建议,希望留下你们的看法,这个思路我花了两个小时整理,两个小时写出来的,希望看过的小朋友都能关注一波,谢谢大家了!

posted @ 2017-10-21 14:09  DemmonTree  Views(477)  Comments(1)    收藏  举报