动态规划—矩阵链乘法

矩阵链乘问题描述

给定n个矩阵构成的一个链<A1,A2,A3,.......An>,其中i=1,2,...n,矩阵A的维数为pi-1pi,对乘积 A1A2...A以一种最小化标量乘法次数的方式进行加全部括号。

注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。

动态规划分析过程

1)最优加全部括号的结构

  动态规划第一步是寻找一个最优的子结构。假设现在要计算AiAi+1....Aj的值,计算Ai...j过程当中肯定会存在某个k值(i<=k<j)将Ai...j分成两部分,使得Ai...j的计算量最小。分成两个子问题Ai...k和Ak+1...j,需要继续递归寻找这两个子问题的最优解。

  有分析可以到最优子结构为:假设AiAi+1....Aj的一个最优加全括号把乘积在Ak和Ak+1之间分开,则Ai..k和Ak+1..j也都是最优加全括号的。

2)一个递归解

  设m[i,j]为计算机矩阵Ai...j所需的标量乘法运算次数的最小值,对此计算A1..n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:

当i==j时:m[i,j] = 0,(此时只包含一个矩阵)

当i<j 时:从步骤1中需要寻找一个k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)。

3)计算最优代价

  虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2.....n。输入序列为:p=<p0,p1,...pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下:

MAXTRIX_CHAIN_ORDER(p)
 2   n = length[p]-1;
 3   for i=1 to n
 4       do m[i][i] = 0;
 5   for t = 2 to n  //t is the chain length
 6        do for i=1 to n-t+1
 7                      j=i+t-1;
 8                      m[i][j] = MAXLIMIT;
 9                      for k=i to j-1
10                             q = m[i][k] + m[k+1][i] + qi-1qkqj;
11                             if q < m[i][j]
12                                then m[i][j] = q;
13                                     s[i][j] = k;
14   return m and s;

MATRIX_CHAIN_ORDER具有循环嵌套,深度为3层,运行时间为O(n3)。如果采用递归进行实现,则需要指数级时间Ω(2n),因为中间有些重复计算。

4)构造一个最优解

第三步中已经计算出来最小代价,并保存了相关的记录信息。因此只需对s表格进行递归调用展开既可以得到一个最优解。书中给出了伪代码,摘录如下:

PRINT_OPTIMAL_PARENS(s,i,j)
   if i== j 
      then print "Ai"
   else
      print "(";
      PRINT_OPTIMAL_PARENS(s,i,s[i][j]);
      PRINT_OPTIMAL_PARENS(s,s[i][j]+1,j);
      print")";

5)编程实现

public class Q1_Matrix_chain {
    public static int[] atest ={30,35,15,5,10,20,25};
    public static int[] a={3, 5, 2, 1, 10};
    public static int[] b={2, 7, 3, 6, 10};
    public static int[] c={10, 3, 15, 12, 7, 2};
    public static int[] d={7, 2, 4, 15, 20, 5};
    public static void main(String[] args)
    {
        System.out.println("<3, 5, 2, 1,10>");
        Matrix_Chain_Order(a);
        System.out.println("<2, 7, 3, 6, 10>");
        Matrix_Chain_Order(b);
        System.out.println("<10, 3, 15, 12, 7, 2>");
        Matrix_Chain_Order(c);
        System.out.println("<7, 2, 4, 15, 20, 5>");
        Matrix_Chain_Order(d);

    }

    public static void Matrix_Chain_Order(int[] a){
        int n = a.length-1;
        int[][] m = new int[n+1][n+1];
        int[][] s = new int[n+1][n+1];
        int i,j,k,t;

        for (i=0;i<=n;i++)
            m[i][i] = 0;
        for (i=0;i<=n;i++)
            s[i][i] = 0;
        for(t=2; t<=n; t++) //t is the chain length
        {
            for(i=1;i<=n-t+1;i++)//从第一矩阵开始计算,计算长度为t的最小代价
            {
                j = i+t-1;//长度为t时候的最后一个元素
                m[i][j] = 1000000;//初始化为最大代价
                for(k=i;k<=j-1;k++)//寻找最优的k值,使得分成两部分k在i与j-1之间
                {
                    int temp = m[i][k]+m[k+1][j] + a[i-1]*a[k]*a[j];
                    if(temp < m[i][j])
                    {
                    m[i][j] = temp;   //记录下当前的最小代价
                    s[i][j] = k;      //记录当前的括号位置,即矩阵的编号
                    }
                }
            }
        }
        System.out.println("一个最优解为:");
        Display(s,1,n);
        System.out.println("
计算的次数为:");
        System.out.println(m[1][n]);
    }
    public static void Display(int[][] s,int i,int j)
    {
        if( i == j)
        {
            System.out.print('A');
            System.out.print(i);
        }
        else
        {
            System.out.print('(');
            Display(s,i,s[i][j]);
            Display(s,s[i][j]+1,j);
            System.out.print(')');
        }

    }

}

 

原文地址:https://www.cnblogs.com/dear_diary/p/6714077.html