Datawhale编程集训第五天

一、递归思想

递归: 在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归。
所有的递归函数都有着同样的结构,这个结构由两部分组成:基础部分,递归部分
递归常与分治思想同时使用,并且递归常用来解决结构相似的问题。所谓结构相似,是指构成原问题的子问题与原问题在结构上相似,可以用类似的方法解决。比如,求一个整数n的阶乘问题;斐波拉契数列问题;汉诺塔问题。 具体地,整个问题的解决,可以分为两部分:第一部分是一些特殊情况,有直接的解法;第二部分与原问题相似,但比原问题的规模小,并且依赖第一部分的结果。。实际上,递归是把一个不能或不好解决的大问题转化成一个或几个小问题,再把这些小问题进一步分解成更小的小问题,直至每个小问题都可以直接解决。
递归两个基本要素:
(1) 边界条件:确定递归到何时终止,也称为递归出口。
(2) 递归模式:大问题是如何分解为小问题的,也称为递归体。

示例:

1、求一个整数n的阶乘问题

#递归版本的阶乘实现:
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

2、斐波拉契数列问题

def fab(n):
    if n < 1:
        print('输入有误...')
        return -1
    elif n == 1 or n == 2:
        return 1
    else:
        return fab(n-1) + fab(n-2)

3、汉诺塔的递归算法

def Hanoi(n, A, B, C) :
  if (n == 1) :
    move(A, c)   #表示只有一个碟子时,直接从A塔移动到C塔
  else :
    Hanoi(n - 1, A, C, B)  #将剩下的A塔上的n-1借助C塔移动到B塔
    move(A, C)              #将A上最后一个直接移动到C塔上
    Hanoi(n - 1, B, A, C)  #将B塔上的n-1个碟子借助A塔移动到C塔

二、动态规划思想

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。
动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。
一般情况下,我们能将问题抽象出来,并且问题满足无后效性,满足最优子结构,并且能明确的找出状态转移方程的话,动态规划是最好的选择。

  • 无后效性通俗的说就是只要我们得出了当前状态,而不用管这个状态怎么来的,也就是说之前的状态已经用不着了,如果我们抽象出的状态有后效性,很简单,我们只用把这个值加入到状态的表示中。
  • 最优子结构(自下而上):在决策问题中,如果,当前问题可以拆分为多个子问题,并且依赖于这些子问题,那么我们称为此问题符合子结构,而若当前状态可以由某个阶段的某个或某些状态直接得到,那么就符合最优子结构
  • 重叠子问题(自上而下):动态规划算法总是充分利用重叠子问题,通过每个子问题只解一次,把解保存在一个需要时就可以查看的表中,每次查表的时间为常数,如备忘录的递归方法。斐波那契数列的递归就是个很好的例子
  • 状态转移:在抽象出上述两点的的状态表示后,每种状态之间转移时值或者参数的变化。

动态规划的本质思想就是递归, 但如果直接应用递归方法, 子问题的答案会被重复计算产生浪费, 同时递归更加耗费栈内存, 所以通常用一个二维矩阵(表格)来表示不同子问题的答案, 以实现更加高效的求解。

示例:

1、背包问题(rubik_wong–0/1背包问题
代码参考如下:

def MaxVal2(memo , w, v, index, last):  
    """ 
    得到最大价值 
    w为widght 
    v为value 
    index为索引 
    last为剩余重量 
    """  
    global numCount  
    numCount = numCount + 1  
    try:  
        #以往是否计算过分支,如果计算过,直接返回分支的结果  
        return memo[(index , last)]  
    except:  
        #最底部  
        if index == 0:  
            #是否可以装入  
            if w[index] <= last:  
                return v[index]  
            else:  
                return 0  
        #寻找可以装入的分支  
        without_l = MaxVal2(memo , w, v, index - 1, last)  
        #如果当前的分支大于约束  
        #返回历史查找的最大值  
        if w[index] > last:  
            return without_l  
        else:  
            #当前分支加入背包,剪掉背包剩余重量,继续寻找  
            with_l = v[index] + MaxVal2(memo , w, v , index - 1, last - w[index])  

        #比较最大值  
        maxvalue = max(with_l , without_l)  
        #存储  
        memo[(index , last)] = maxvalue  
        return maxvalue  

w = [0, 1, 4, 3, 1]   # 东西的重量 
v = [0, 1500, 3000, 2000, 2000]       # 东西的价值

numCount = 0  
memo = {} 
n = len(w) - 1
m = 4
print MaxVal2(memo , w, v, n, m) , "caculate count : ", numCount  

# 4000 caculate count :  20

2、XY的网格迷宫
有一个X
Y的网格,小团要在此网格上从左上角到右下角,只能走格点且只能向右或向下走。请设计一个算法,计算小团有多少种走法。给定两个正整数int x,int y,请返回小团的走法数目。
输入描述:
输入包括一行,逗号隔开的两个正整数x和y,取值范围[1,10]。

输出描述:
输出包括一行,为走法的数目。

示例1
输入
3 2

输出
10

# 动态规划,使用递推方程d[i][j] = d[i-1][j] + d[i][j-1]
# 因为可能从两个方向走到同一个点,所以从上到下为一种走法,从左到右是另一种走法
# 注意题目给的是x*y方格,所以是(x+1)*(y+1)个点


line = map(int, raw_input("").split(" "))
x = line[0]
y = line[1]

d = [[0]*(y+2) for i in range(x+2)]

for i in range(1,x+2):
    for j in range(1,y+2):
        if i==j and i==1:
            d[i][j] = 1
        else:
            d[i][j] = d[i-1][j] + d[i][j-1]

print d[-1][-1]

参考内容:

https://blog.csdn.net/MrLevo520/article/details/75676160
https://blog.csdn.net/rubik_wong/article/details/54854547
https://blog.csdn.net/qianggezhishen/article/details/47030475
https://blog.csdn.net/qq_26567507/article/details/80768329
https://blog.csdn.net/qq_33848737/article/details/77466665
http://lib.csdn.net/article/python/3227
http://www.cnblogs.com/liuxiaowei/p/7262428.html
https://cloud.tencent.com/developer/article/1355139
https://www.cnblogs.com/jeavenwong/p/6896576.html
http://www.cnblogs.com/linshuhui/p/9680146.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值