动态规划矩阵链(1)

最近看研三的找工作,主要变成题目都是过程实现,模拟一下,稍微带点技巧,算法层面也就是动态规划了,想想自己也一直忙于实验室各种事情,也该回来看看算法了,现在迁移一些以前写过的内容,顺便做复习。

矩阵链乘法也就为连续相乘的矩阵加括号,以最少的计算次数完成计算。即现有多个连续矩阵相乘A1A2A3...An,对于这样的n个矩阵相连,采取暴力法,按照排列组合在矩阵之间插入分隔符,可以选择的种类即有2^{n-1}种。想必这样的方法是难以实现的,如果不会动归,都是采取空间搜索算法,遗传算法去做吧。

动态规划嘛,个人理解也就是无限贪心,打一张表,在改变一点结构后,总能找到上一个最优结构,即最优解是可以递归定义的,这里摘录一下算法导论上的原话:

如何制定动态规划方案:
1. 刻画一个最优解的结构特征
2. 递归的定义最优解的值
3. 计算最优解的值,通常采用自底向上方法
4. 利用计算出的信息构造出一个最优解

那么先从整体的角度来看,假设说前面的分割都非常优秀了,就差最后一步,需要在第k个位置进行切割,那么就是在A1-An之间选出一个k,之后计算过程为(A1A2...Ak)(Ak+1Ak+2...An)。那么在A1-Ak之间同样可以继续选k‘,那么,我们就可以列出式子:

                              m[i][j]=\left\{\begin{matrix}0 , i==j \\ min\{m[i][k]+m[k+1][j]+a[i].row*a[k].col*a[j].col\},i\leq k < j \end{matrix}\right.

其中m[i][j]表示Ai和Aj中间的最小计算次数,也就等于Ai和Ak之间的最小计算次数加上,Ak+1和Aj之间的最小计算次数,a[i].row*a[k].col*a[j].col也就是最后生成的左边矩阵a[i].row行(m1)乘a[k].row列(n1),右边矩阵a[k+1].row行(m2)乘a[j].col列(n2)的矩阵,左矩阵某一行乘右矩阵某一列是一个数字的计算次数,总共即有m1*n1*n2次乘法运算。

已经写出了递归式,我们开始计算最优解的值,采取自底向上方法,首先根据递归式,将M[i][i]全部填0。接下来设计自底向上填的方法。由递归式可以看出,我们要填写M[i][j]就需要知道M[i][k], M[k+1][j]举个例子M[2][4] 需要知道M[2][2],M[2][3], M[3][4], [m[4][4]。再举一个例子M[1][3],需要知道M[1][1], M[1][2], M[2][3], M[3][3]。

以五个元素为例,先画出填0的部分
          m[1][1] m[2][2] m[3][3] m[4][4] m[5][5]
根据上图,现在考虑m[1][2],m[1][2]需要知道m[1][1],m[2][2], 那么m[2][3]需要知道m[2][2],m[3][3]。由此,m[3][4]需要知道m[3][3],m[4][4],补充上图
          m[1][2] m[2][3] m[3][4] m[4][5]
        m[1][1] m[2][2] m[3][3] m[4][4] m[5][5]
现在看这张图就感觉好一些了,那么跟着数字往上,现在考虑m[1][3],m[2][4],m[3][5],根据已经举过的数据,来继续画图
            m[1][3] m[2][4] m[3][5]
          m[1][2] m[2][3] m[3][4] m[4][5]
        m[1][1] m[2][2] m[3][3] m[4][4] m[5][5]
可以看出来需要知道的数据是一个小三角形,很好推测,接下来的数据会是什么,那么画完这个大三角形吧
              m[1][5]
            m[1][4] m[2][5]
          m[1][3] m[2][4] m[3][5]
        m[1][2] m[2][3] m[3][4] m[4][5]
      m[1][1] m[2][2] m[3][3] m[4][4] m[5][5]
根据递归式也能够看出这个结果。最优解就是m[1][5]的取值。

接下来考虑程序怎么编写,从最下面开始,填完了m[same][same]。

开始第二层,容易看出这一层m[i][j]之间j – i = 1,实际每一层都是有相同的数字差。这个数字差值在意义上也就是长度
那么我们设置一个变量就叫len,它的变化应该放在最外层循环中,循环n次(n表示矩阵数目)接下来循环变化的就是变量i了,i每次从1开始一直到n-len之后是j, j总是= i + len;

现在,遍历的循环任务就结束了,开始考虑如何计算,也就是怎么样做这个三角形。设置变量k,观察三角形,根据递归式,k的值就是从i-j,那么就这样设置了。根据递归式,可以轻松写下k这个最内层循环的内容,如此,这个程序核心部分就结束了,最后,贴上代码。

#define NUM 10

int M[NUM][NUM];
int S[NUM][NUM];

struct Matrix{
   int x;
   int y;
};

void Print(int i, int j)
{
     if(i == j)
        printf("A");
     else
     {
       printf("(");
       Print(i,S[i][j]);
       Print(S[i][j]+1,j);
       printf(")");
     }
}

void Matrix_Chain_Order(struct Matrixa[NUM], int index_max)
{
    unsigned int i,j,l,k,q;
    unsigned int max_int = 0 - 1;
    for(i = 0;i < index_max;++i ) 
    M[i][i]= 0;
    for(l = 1;l <= index_max; ++l) //length = l + 1;
    for(i = 0;i <= index_max - l;++i)
    {
        j= i + l;
         M[i][j]= max_int / 2;
         for(k = i;k < j;++k)
         {
            q= M[i][k] + M[k+1][j] + a[i].x * a[k].y * a[j].y;
            if(q < M[i][j])
            {
                M[i][j]= q;
                S[i][j]= k;
            }  
         }
      }
   Print(0, index_max);
   printf("\n");
}

int main()
{
     struct Matrix exc_ma[NUM];
     int n;
     int i, j;
     scanf("%d",&n);
     for(int i = 0;i < n; ++i)
     scanf("%d%d",&exc_ma[i].x,&exc_ma[i].y);
     Matrix_Chain_Order(exc_ma,--n);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值