矩阵连乘-动态规划

动态规划的基本思想

将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。

问题描述:

 给定n个矩阵:A1,A2,...,An,其中Ai与Ai+1是可乘的,i=1,2...,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少

问题解析:

  由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。

       完全加括号的矩阵连乘积可递归地定义为:

     (1)单个矩阵是完全加括号的;

     (2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)

     

 例如,矩阵连乘积A1A2A3有2种不同的完全加括号的方式:

((A1*A2)*A3), (A1*(A2*A3))

每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。

A1*A2  A1:10*25, A2:25*35  则A1*A2=10*25*35 即两个矩阵相乘等于第一个矩阵的行数*列数*第二个矩阵的列数

看下面一个例子,计算三个矩阵连乘{A1,A2,A3};维数分别为10*100 , 100*5 , 5*50

按此顺序计算需要的次数((A1*A2*A3):10X100X5+10X5X50=7500次,

按此顺序计算需要的次数(A1*(A2*A3)):10*5*50+10*100*50=75000次

能用动态规划的一个性质就是最优子结构性质,也就是说计算A[i:j]的最优次序所包含的计算矩阵子链A[i:k]和A[k+1:j]的次序也是最优的。动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算(即先从最小的开始计算)。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法。我们可以根据下面这个公式来计算结果。m[i,j]表示矩阵i到矩阵j连乘的最小次数,其中p[i-1]表示的是第i个矩阵的行数,p[k]表示i:k矩阵合起来后最后得到的列数,p[j]是k+1:j合起来后得到的列数。

 

求解步骤:

假设p={10,5,2,8,4,9,10}; 表示有六个矩阵,维数分别为:10*5  5*2  2*8  8*4  4*9  9*10

从连乘矩阵个数为2开始计算每次的最小乘次数m[i][j]:

 m[1][2] m[2][3] m[3][4]  m[4][5]     //m[1][2]表示第一个矩阵与第二个矩阵的最小乘次数

然后再计算再依次计算连乘矩阵个数为3:

m[1][3] m[2][4] m[3][5] m[4][6]

连乘矩阵个数为4:

m[1][4] m[2][5] m[3][6]

连乘矩阵个数为5:m[1][5] m[2][6]

连乘矩阵个数为6:m[1][6]    //即最后我们要的结果

代码实现:

package dongtaiguihua;
public class MatrixChain1 {

    /**
     * 此方法用来求解矩阵连乘的最小数乘次数
     */
    public static void matrixChain(int p[]) {
        int n=p.length-1+1;//p.length-1:矩阵的个数,+1:即定义二维数组的大小,A1-An,s[0][:]=0,s[:][0]=0;
        int[][] m=new int[n][n];//保存Ai-Aj 矩阵连乘的最小个数
        int[][] s=new int[n][n];//保存矩阵连乘最小情况的断开位置
        for (int i=1;i<n;i++)
        {
            m[i][i]=0;//即A1,A2...An单个矩阵连乘的个数为0
        }
        for(int r=2;r<n;r++)//r为当前计算的链长(子问题规模)
        {
            for(int i=1;i<=n-1-r+1;i++)//n-1:总的矩阵个数,n-1-k+1:最后一个r链的前边界
            {
                int j=i+r-1;
                m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//将Ai:Aj分成A(i)*A[i+1:j]
                s[i][j]=i;//记录断开的位置
                for(int k=i+1;k<j;k++)
                {
                    int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                    if(temp<m[i][j])
                    {
                        m[i][j]=temp;
                        s[i][j]=k;
                    }
                }
            }
        }
        matrixDivide(1,n-1,s);
        System.out.println(m[1][n-1]);
    }
    public static void  matrixDivide(int i,int j,int[][] s)//矩阵的断开位置
    {
        if(i==j)
        {
            return;
        }
        matrixDivide(i,s[i][j],s);
        matrixDivide(s[i][j]+1,j,s);
        int x = s[i][j] + 1;
        System.out.print("Multipy A" + i + "," + s[i][j]);
        System.out.println(" and A" + x + "," + j);
    }
    public static void main(String[] args)
    {
        int[] p={10,5,2,8,4,9,10,7,6};//传入的要连乘的矩阵的维数信息的数组
        matrixChain(p);
    }
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值