递归
递归的定义
1、至少有一个明确的递归结束条件;
2、给出递归终止时的处理办法;
3、每次进入更深一层递归时,问题规模(计算量)相比上次递归都应有所减少
递归的应用
递归结构最常用在树结构中,例如先序遍历;其次用在回溯法中,递归可以撤销之前的操作;同时递归还是动态规划的一种超集,其名为记忆化递归,记忆化递归在很多场景下可以替换动态规划。
常见的例子
1.斐波那契数列
def fab(n):
if n <= 2: # 边界条件
v = 1
return v
v = fab(n-1)+fab(n-2) # 求解子问题
return v
print(fab(5))
2.阶乘
def fact(n):
if n==1:
return n
n = n*fact(n-1)
return n
print(fact(5))
3.最大公因数
def gcd(m, n):
if n == 0:
return m
else:
return gcd(n, m%n)
print(gcd(6,4))
更多例子:
Python实例解读,帮你理解递归算法 - 知乎 (zhihu.com)
Python中递归的几个例子_python 递归例子-CSDN博客
记忆化递归
原理:
因为普通的递归可能会重复求解某一值,类似斐波那契数列。同样的子问题可能会被求解多次,这样就会很慢很慢很慢。
解决方法:我们把历史求解(子问题)记录下来,如果下次需要求解子问题,那么直接取出就好。其时间复杂度为O(1)。
1.斐波那契数列
红色区域是边界区域。蓝色区域是为了求解子问题。绿色区域是当问题求结果,则直接返回。
m = [0] * 10000 # 开辟一个数组来存储,或者使用字典
def f(n):
if n <=2:
return n
if not m[n]:
m[n] = f(n - 1) + f(n -2)
return m[n]
print(f(5)) # 5
和自己的草稿有点对不上,,求大佬赐教
2.找硬币
如果有观众建议直接去看下面这位大佬的。此处只为汇总给自己看,并无更新内容。
【数据结构与算法python】递归算法的记忆化存储-CSDN博客
假设你为一家自动售货机厂家编程序,自动售货机要每次找给顾客最少数量硬币;
例如,某次顾客投进$1纸币,买了ȼ37的东西,要找ȼ63,那么最少数量就是: 2个quarter(ȼ25)、 1个dime(ȼ10)和3个penny(ȼ1),一共6个
单纯用递归方法,速度慢:
首先是确定基本结束条件, 兑换硬币这个问题最简单直接的情况就是, 需要兑换的找零, 其面值正好等于某种硬币,如找零25分,答案就是1个硬币!
其次是减小问题的规模, 我们要对每种硬币尝试1次, 例如美元硬币体系:
找零减去1分(penny)后,求兑换硬币最少数量(递归调用自身);
找零减去5分(nikel)后,求兑换硬币最少数量
找零减去10分(dime)后,求兑换硬币最少数量
找零减去25分(quarter)后,求兑换硬币最少数量
上述4项中选择最小的一个。
最后,就是去调用函数自身。
import time
def recMC(coinValueList,change): # 输入硬币面值列表、找零之和
minCoins = change # 初始化
if change in coinValueList: # 边界条件
return 1
else:
for i in [c for c in coinValueList if c<=change]:
numCoins = 1+recMC(coinValueList,change-i) # 递归,缩小问题规模
if numCoins <= minCoins: # 更新最小硬币数
minCoins = numCoins
return minCoins
print(time.time())
print(recMC([1,5,10,25],63))
print(time.time())
改进:
对这个递归解法进行改进的关键就在于消除重复计算,我们可以用一个表将计算过的中间结果保存起来,在计算之前查表看看是否已经计算过,这个算法的中间结果就是部分找零的最优解, 在递归调用过程中已经得到的最优解被记录下来,在递归调用之前,先查找表中是否已有部分找零的最优解,如果有, 直接返回最优解而不进行递归调用,如果没有,才进行递归调用
这种方法叫做“memoization(记忆化/函数值缓存) ”的技术,能提高递归解法的性能。
import time
def recDC(coinValueList,change,knownResults):
minCoins = change
if change in coinValueList:
knownResults[change] = 1 # 边界条件
return 1
elif knownResults[change]>0: # 最优中间值
return knownResults[change]
else:
for i in [c for c in coinValueList if c<=change]:
numCoins = 1+recDC(coinValueList,change-i,knownResults)
if numCoins <= minCoins:
minCoins = numCoins
knownResults[change] = minCoins # 更新硬币更少的组合
return minCoins
print(time.time())
print(recDC([1,5,10,25],63,[0]*64))
print(time.time())
速度真的差距很大,,只不过加一个表,,这个方法还是要学一下