python 递归、记忆化递归、递归法模板

递归

递归的定义

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博客

记忆化递归

教女朋友递归与记忆化递归 - 知乎 (zhihu.com)

原理:

因为普通的递归可能会重复求解某一值,类似斐波那契数列。同样的子问题可能会被求解多次,这样就会很慢很慢很慢。

解决方法:我们把历史求解(子问题)记录下来,如果下次需要求解子问题,那么直接取出就好。其时间复杂度为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())

速度真的差距很大,,只不过加一个表,,这个方法还是要学一下

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值