最优化问题的解法 - 动态规划

1

虽是读书笔记,但是如转载请注明出处 http://segmentfault.com/blog/exploring/
.. 拒绝伸手复制党

以下是算法导论第15章的学习笔记


动态规划常用于最优化问题。可能存在多个取最优解的值,希望找到其中一个最优解。

[摘自安勃卿的 BLOG][1]
动态规划与分治法类似,也是将问题分解为若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

分治法在问题较大且相互不独立的情况下,由于分解得到的子问题数目太多,各个递归子问题被重复计算多次,求解过程呈幂级数增长,其时间复杂度为 n 的指数时间。
与分治法不同,动态规划方法采用自底向上的递推方式求解,并且经分解得到的子问题往往不是相互独立的,根据子问题的相关性,在每步列出可能的局部解中选出能产生最佳解的部分,并将计算过程填表,只要某个子问题被解决, 将不会被多次计算,从而减少了算法的时间复杂度。

求解问题时每次决策依赖于当前状态,又随即引起状态的转移,决策序列在变化的状态中逐步产生,这种用多阶段最优化决策方式解决问题的过程称为动态规划。

递推关系 (状态转移方程) 是实现由分解后的子问题向最终问题求解转化的纽带。 填表技术是把计算过的所有子问题的解都记录下来,由于将原问题分解后的各个子问题可能存在重复性,所以当以后重复遇到该子问题时,只需要查表继续问题的求解,而不需要重复计算,这样就节省了很多计算时间,这就是动态规划的精髓。

动态规划的设计分为以下四个步骤:

  • 描述最优解结构
  • 递归定义最优解的值
  • 自底向上的方式计算最优解的值
  • 由计算出的结果构造一个最优解

动态规划的基本要素(如何判断一个问题可以应用动态规划方法的标志)
1. 一个问题的最优解包含了子问题的一个最优解。

动态规划方法的关键在于正确地写出基本的递推关系式和恰当的边界条件。要做到这一点,就必须将原问题分解为几个相互联系的阶段,恰当的选取状态变量和决策变量及定义最优值函数,从而把一个大问题转化成一组同类的子问题,然后逐个求解。即从边界条件开始,逐阶段递推寻优,在每一个子问题的求解中,均利用它前面的子问题的最优化结果,依次进行,最后一个子问题所得的最优解就是整个问题的最优解,这样就说明具有最优子结构
  1. 子问题重叠。一个递归算法在其递归树的不同分支中可能会多次遇到同一个子问题。
   当一个递归算法不断地调用同一问题时,则该最优问题包含重叠子问题。
   `动态规划`算法总是充分利用重叠子问题,即通过每个子问题只解一次,把解保存在一个在需要时就可以查看的`表`中。
   而`分治法`解决的问题往往在递归的每一步都产生全新的子问题,且对递归树中重复出现的每个子问题都要重复解一次。

动态规划的时间复杂度:
依赖于两个因素的乘积:1. 子问题的总个数 2. 每个子问题有多少种选择。
装配线问题一共有O(n)个子问题,每个问题2个选择.故复杂度O^3
矩阵链乘法一共有O(n^2)个子问题,每个问题n个选择.故复杂度O^3


实际问题:
1. 装配线调度
寻找最优解结构:
i=1时候问题的情况;
i>1时候问题的情况;
发现最优子结构可以利用子问题的最优子结构构造原问题的一个最优解。
S1,jS1,j-1的关系;或者S2,j-1的关系。

自顶向下再自顶向上的递归方法时间复杂度达到O(2^n);而直接从自底向上,可以线性时间解决问题。
2. 矩阵链乘法
问题描述:给定n个要相乘的矩阵构成的序列链<A1,A2,...,An>,要计算乘积A1,A2,...,An
可将两个矩阵相乘的标准算法作为一个子程序,根据括号给出的计算顺序做出全部的矩阵乘法。不同的加全部括号顺序所带来的矩阵乘法的代价不同。
比如:
(A1,(A2(A3,A4)))
...
(A1(A2A3)A4)

所以矩阵链乘法问题实际上是,要求得到一种最小化标量乘法次数的方式进行加全部括号。即确定具有最小代价的矩阵相乘顺序。该问题的最优子结构如下:假设 A1 A2...An 的一个最优加括号把乘积在 AkAk+1 间分开,则前缀子链 A1...Ak 的加括号方式必定为 A1...Ak 的一个最优加括号,后缀子链同理。

设矩阵Ai的维数pi-1 * pi
图片描述

使用自底向上的表格法来计算最优代价。

先贴一段这块用到的两个矩阵相乘的代码:

//矩阵相乘
public class MatrixMultiply {
    // 矩阵的乘法
    public int[][] martirxMultiply(int A[][], int B[][]){
        int result [][] = new int [A.length][B[0].length];
        if(A[0].length != B.length){
            System.out.println("test");
            return result;
        }
        else{
            // result 的每一行
            for(int i=0;i<A.length;i++)
                // result 的每一列
                for(int j=0;j<B[0].length;j++)
                    // 对每个元素的计算
                    for(int k = 0;k<A[0].length;k++)
                        result[i][j] = result[i][j] + A[i][k]*B[k][j];
        }
        return result;
    }

    public static void main(String[] args) {
        MatrixMultiply m = new MatrixMultiply();
        int[][] a={{1,2},{3,4},{5,6}};// 自己定义矩阵   
        int[][] b={{1,2,3},{4,5,6}};
        int [][] result = m.martirxMultiply(a, b);
        for(int i=0;i<a.length;i++)
            for(int j=0;j<b[0].length;j++)
                System.out.println(result[i][j]);
    }
}

你可能感兴趣的

载入中...