• 动态规划Dynamic Programming(0)——算法介绍


      动态规划(Dynamic Programming)可以用来非常有效的解决许多搜索优化问题。这类问题都有相同的地方,原问题可以分解为子问题,子问题中有重复的部分(overlapping subproblem),或者原问题的最优解与子问题的最优解具有相同的结构(optimal substructure),将规模较小的子问题的最优解扩展为规模较大的问题的最优解。
    它之所以有效,是因为它采用自底向上的方式递推求值,并把中间结果存储起来以便以后用来计算所需要求的解。
      动态规划虽然比较容易理解,在应用过程中可以分为两步,先找出子问题的最优结构,然后找出子问题的最优解向规模较大子问题的最优解扩展。但在具体应用中会有各种各样的问题。下面将讨论各种动态规划问题,希望能加深对动态规划的理解。
      动态规划典型问题之一,Feibonaci数列
      f(1)=f(2)=1,f(n)=f(n-1)+f(n-2);
      直接求解,用递归方式,可以方便的写出,

    def fib(n):
        if n==1: return 1
        elif n==2: return 1
        else: return fib(n-1)+fib(n-2)

      计算f(5),展开调用过程,如下,
        f(5) = f(4) + f(3)
        = f(3) + f(2) + f(2)+f(1)
        = f(2)+f(1) + f(2) + f(2)+f(1)
      可以看到,计算f(4)的时候会调用f(3),f(4)计算玩之后,又要调用f(3),里面有大量的重复计算。因为是递归调用,如果写成循环,从底向上计算,

    def fib(n):
        if n==1: return 1
        elif n==2: return 1
        else:
            f = [1 for i in xrange(n)]
            for i in xrange(2,n):
                f[i]=f[i-1]+f[i-2]
            return f[n-1] 

      存储空间为n个,实际上,计算f(n)时只与前两项n-1和n-2相关,没有必要保存n项,所以代码可以简化如下,

    def fib(n):
        n2, n1 = 0, 1
        for i in range(n-2): 
            n2, n1 = n1, n1 + n2
        return n2+n1

      这里定义了n=0时f(0)=0,参考http://20bits.com/article/introduction-to-dynamic-programming

      Feibonaci数列问题,子问题有很多重复,换为自底向上计算,避免很多重复,计算效率提高。对于最优子结构的问题,可以以整数序列子序列最大和的问题为例,这里与刚才的一篇参考重合,不过还是这个例子比较好说明,就它了。

      整数子序列最大和问题
      给定一个整数序列,找出子序列的最大和。例如array=[1,2,-5,4,7,-2],我们可以直接看出,子序列最大和为4+7=11,那么计算机怎么求解呢?
    求出所有子序列的和?子序列个数n(n+1)/2=sum(i,i=1,..n),复杂度n^2。

      代码如下,

    def msum(a):
        return max([(sum(a[j:i]), (j,i)) for i in range(1,len(a)+1) for j in range(i)])

      有没有简单一点的方法呢?
      动态规划。如果使用动态规划,要求n的解,从n-1的解扩充到n的解。如果知道n-1个数中最大子序列的和,增加一个数到n个数,最大子序列的和是什么呢?如果考虑这增加的一个数a[n],如果a[n]>=0,那么可以认为是增加和,那么最大子序列应该加上这个数。但是如果前n-1个数的最大子序列的和对应的子序列与a[n]不连续,那么怎么办呢?
    换个角度考虑。如果考虑前n-1项最大子序列的和sum,如果sum<0,那么与后面的数相加,就会使最大子序列的和减小。
    例如,a[j:k]是序列a[j:i]的最优子序列,s是最优子序列的和,t是a[j:i]的和,那么考虑下一个元素a[i+1],如果t+a[i+1]>=s,那么a[j:i+1]应该作为最优子序列,s=t+a[i+1],t=s;然而,如果t+a[i+1]<0,那么a[j:i+1]就不应该在最有子序列中,因为它会使子序列的和减少,所以此时,t=0,从下一个元素开始,对a[i+2:n],解新的问题。需要注意的是,s还是继续,保存最优子序列的和。
      代码如下,

    def msum2(a):
        bounds, s, t, j = (0,0), -float('infinity'), 0, 0
        
        for i in range(len(a)):
            t = t + a[i]
            if t > s: bounds, s = (j, i+1), t
            if t < 0: t, j = 0, i+1
        return (s, bounds)

      参考链接里的代码又被我搬过来了,看着有写好的代码,自己就没有写的动力了。
      使用动态规划算法的时间复杂度为O(n),只要对序列扫描一遍就可以了。

      从这两个典型问题,可以看出动态规划问题的要义,把大问题化为小问题,在计算小问题的时候,避免重复计算,从小问题的解扩展到大问题的解的时候,要搞明白如何扩展,搞明白子问题的最有子结构是什么。
      动态规划的思想虽然已经理解了,但遇到具体的问题的时候,又会有各种问题,所以我想尽可能多的列举动态规划问题,并给出解答。动态规划系列从这一篇开始,之后各种例题。

  • 相关阅读:
    浅谈.NET和JAVA的跨平台
    ADO.NET获取TIPTOP存储过程的返回值
    Hide DataGrid Columns via HeaderText
    笑话一则:开车的最高境界
    [推薦]面试时最常问的15问题
    美国小学生守则 VS 中国小学生守则
    Embedded UserControls: Revisited
    SOA认识存误区 详解SOA企业部署的六大关键要素
    Java、.NET,为什么不合二为一?
    [轉]informix语句祥解
  • 原文地址:https://www.cnblogs.com/Frandy/p/dynamic_programming_0.html
Copyright © 2020-2023  润新知