zoukankan      html  css  js  c++  java
  • 动态规划算法入门

    1. 动态规划算法定义:

        动态规划,英文描述为Dynamic programming. 是一种可以把原始问题分解为若干相关联的子解问题,并通过求取和保存子问题的解,获得原问题的解。

        动态规划算法可以解决的问题通常包含如下特征:

    • 重叠子问题
    • 最优子结构

      对于第一个特征,比较容易理解,即分解的若干子问题,包含着重复的解。举例如:斐波那契数列,F(n) = F(n-1) + F(n-2), 求解的F(n-1)的过程中,包含着求解F(n-2)的结果。

          对于第二个特征,参考网上的说法为:

           假设当前决策结果是f[n],则最优子结构就是要让f[n-k]最优,最优子结构性质就是能让转移到n的状态是最优的,并且与后面的决策没有关系,即让后面的决策安心地使用前面的局部最优解的一种性质。

    关键字解读为:  

    •  当前的决策与后面的决策是无关的,  
    •   f[n-k]是最优的,转移到f[n]的状态是最优的

        2. 动态规划算法的一般步骤和难点

      使用动态规划算法解决问题的一般步骤是:

    •  找到问题的最优解的性质,用数学公式或者算法描述
    •  拆解子问题,确定问题的递推结构,保证可以收敛。

      用知乎大神们的总结就是:找到问题的状态描述和状态转移方程。

        3. 动态规划算法的分类和理解

      根据我的理解,以及网上的说法,我把动态规划算法分为三个类别和层次:

    • 简单动态规划算法,即状态方程是用一个维度的变量的描述的,常见的问题如:斐波那契数列,爬台阶问题等

      爬台阶问题问题描述: 有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。

      状态描述: 我们使用变量n表示台阶的级数,F(n)表示n级台阶一共有多少种走法

      状态转移方程与问题分解: 根据每次能跨越的台阶数目:1级台阶或者2级台阶,因为走到N级台阶之前,人一定是处于N-1级台阶或者N-2级台阶。F(n)的走法,一定是n-1级别的台阶的所有的走法和n-2级别台阶的所有走法之和。

      F(n) = F(n-1) + F(n-2);  关于状态的分解,更详细的说明,可以看这篇文章:http://blog.csdn.net/baidu_37107022/article/details/73188963。 作者讲的非常的通俗易懂。佩服这么辛苦的编辑。

         Java的代码实现

    public static int  getSumStep(int n){
            if(n < 1){
                return 0;
            }
            else if(n == 1){
                return 1;
            }
            else if(n == 2){
                return 1;
            } else {
                int f1 = 1;
                int f2 = 1;
                int f = 0;
                for(int i=3; i<=n; ++i){
                    f = f1 + f2;
                    f1 = f2;
                    f2 = f;
                }
                return f;
            }
        }
    • 二维的变量变化的动态规划算法,即最优解和递推关系需要两个维度变量来描述的,比如01背包问题,两个字符串的公共子序列问题

         这类问题通常需要两个维度的变量,状态的描述比较晦涩,不容易理解,递推关系不是很直观。我自己的学习方法是牢记一个例子,这里以01背包问题为例:

    问题描述:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,5,它们的价值分别是3,4,5,6,现在给你个承重为8的背包,如何让背包里装入的物品具有最大的价值总和?

    编号 a b c d
    w(重量) 2 3 4 5
    v(价值) 3 4 5 6

         

        这类问题我觉得抽象的比较好的一篇文章是这篇文章:

         http://www.cnblogs.com/Christal-R/p/Dynamic_programming.html, 不过我当时是在手机上看到的,好了好久才找到这篇文章。

        作者抽象的实在太好了,我觉得我都没法用语言去写出这么严格的数学公式表达和证明,这里就不赘述了。

        下面写的,仅供自己理解使用, 总结下来就是:

        Xi的取值为0,1 ;表示物品是否选取, i的取值为 1,2,3,4表示a,b,c,d4见物品

        Wi表示物品的重量, w1=2, 表示 a物品的重量为2 

        Vi 表示物品的价值, v3 - 5, 表示物品c的价值为5;

        

          其中n 表示 前 n个物品,这个表述是很重要的,如果是第一次思考这个问题,很多人都会卡在这里,

          m表示背包的重量;

          约束条件: 

         

           递推关系: 

           

          第一个公式表示  n == 0 或者 m == 0 ,  即物品的数量为0 或者背包的重量为0的时候,可以算是起始条件

          第二个公式表示: 表示包的重量小于新增加的物品, 新增加的物品,无法装入,如下图的F(2, 2 ) 表示前两个物品,包的重量2 ,  2  < (w[2] = 3), 此时F(2,2 )= F(1 , 2) = 3;

          第三个公式表示: 包的重量能够容纳w[n],新增加的物品,这个时候,最大的价值就要在 F(n-1, m) 和 F(n-1, m- Wn) + V[n]) 这两个价值中选取了。

          举例如下图打表的 F(4, 8),  因为 8 - (w[n] ,4) > 0 F(4, 8) = max(F(3, 8), F(3,3 ) + v[4]) = 10;

          表的过程如下:

          

          java代码如下:

         

    public static int getMaxValue(int[] wArray, int[] vArray, int bagWeight){
    
            int lenght = wArray.length;
            // init set zero
            // manipulator the talbe
            int [][] result = new int[lenght+1][bagWeight+1];
            int [][] bRecord = new int[lenght+1][bagWeight+1];
    
            for(int i=1; i<= lenght; ++i){
                for(int j=1;j <= bagWeight; ++j){
                    if(j<wArray[i-1]){
                        result[i][j] = result[i-1][j];
                        bRecord[i][j] = 1;
                    }else{
    
                        if( result[i-1][j] > result[i-1][j-wArray[i-1]]+ vArray[i-1]) {
                            result[i][j] = result[i-1][j];
                            bRecord[i][j] = 1;
                        } else{
                            result[i][j] = result[i-1][j-wArray[i-1]]+ vArray[i-1];
                            bRecord[i][j] = 2;
                        }
    
                    }
                }
            }
    
            return result[lenght][bagWeight];
            //return bRecord;
        }

         需要注意的是因为java数组的索引下标为从0,开始,所以

       result[i][j] = result[i-1][j-wArray[i-1]]+ vArray[i-1];

    brecord是记录操作的过程,用于回溯使用,这部分代码,后续实现。
    • 带有额外条件的动态规划问题(这类问题,我暂时还没有学习)

        4.   动态规划与分治法的区别和联系

          分治法是指将问题划分成一些独立地子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解。

         动态规划适用于子问题独立且重叠的情况,也就是各子问题包含公共的子子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。

         分治法主要在于子问题的独立性,比如排序算法等, 动态规划算法主要适用于处理 子问题重复性和最优子结构的的问题。  

         目前的理解还比较浅显,只能先这么记录了。

    天道酬勤、地道酬善、人道酬诚、商道酬信、业道酬精。
  • 相关阅读:
    Civil 3D 二次开发 创建Civil 3D 对象—— 01 —— 创建几何空间点
    Civil 3D 二次开发 创建Civil 3D 对象—— 00 ——
    Civil 3D 二次开发 创建AutoCAD对象—— 01 —— 创建直线
    Civil 3D 二次开发 新建CLR项目出现错误C2143
    Civil 3D 二次开发 创建AutoCAD对象—— 00 ——
    了解AutoCAD对象层次结构 —— 6 ——块表记录
    datepicker97使用
    使用angular 外接 templateUrl,使用ng-include
    angularJs 遮罩
    网上找的有关css兼容问题
  • 原文地址:https://www.cnblogs.com/xiaotuan/p/7260111.html
Copyright © 2011-2022 走看看