一、递归思想
递归: 在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归。
所有的递归函数都有着同样的结构,这个结构由两部分组成:基础部分,递归部分。
递归常与分治思想同时使用,并且递归常用来解决结构相似的问题。所谓结构相似,是指构成原问题的子问题与原问题在结构上相似,可以用类似的方法解决。比如,求一个整数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的网格迷宫
有一个XY的网格,小团要在此网格上从左上角到右下角,只能走格点且只能向右或向下走。请设计一个算法,计算小团有多少种走法。给定两个正整数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