递归函数就是函数内部调用自身,可以使代码逻辑更加易懂。但是递归也有坑,需要避免。

13.1 概念

  • 在函数内部,可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。

  • 理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示:

def fact(n):  if n==1:      return 1  return n * fact(n - 1)

13.2 写递归代码的套路

写递归代码的关键就是找到如何将大问题分解为小问题的规律,然后按照下面套路即可实现:

  • 第一步,写出递推公式

以计算阶乘为例,递归公式是:fact(n)=n!=(n−1)×⋅⋅⋅3×2×1=n×(n−1)!=n×fact(n−1)

  • 第二步,推敲终止条件

以计算阶乘为例,终止条件是n=1时,fact(1)=1。

13.2.1 斐波那契数列

再来看一个斐波那契数列的例子,斐波那契数列中后一个元素是前两个相邻元素的和。比如:

0,1,1,2,3,5,8,13,21,34,55,…。

那么我们如何得到第n个数是多少?分两步走:

第一步,写出递推公式。求第n个元素,可以先求出n-1和n-2个元素的值,然后再将这两个求和,所以公式是:

fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)

第二步,推敲最终终止条件。终止条件包含三个:n=0时,f(n)=0;n=1时,f(n)=1;n=2时,f(n)=1。

if n < 1:  # 递归终止条件   return 0if n in [1, 2]:  # 递归终止条件   return 1

转换成完整代码就是:

def fibonacci(n):    if n < 1:  # 递归终止条件        return 0    if n in [1, 2]:  # 递归终止条件        return 1     return fibonacci(n - 1) + fibonacci(n - 2)  # 递归公式

不管是编写递归还是阅读递归代码,只要遇到递归,我们就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑搞清楚计算机每一步都是怎么执行的。

13.2.2 n 个台阶有多少种走法

再来看看一个例子,假如有 n 个台阶,每次可以跨 1 个台阶或者 2 个台阶,请问走这 n 个台阶有多少种走法?

我们从第一步开始想,如果第一步跨1个台阶,问题就变成了n-1个台架有多少种走法。如果第一步跨2个台阶,问题就变成n-2个台阶有多少种走法。我们把n-1个台阶的走法和n-2个台阶的走法求和,就是n个台阶的走法。用公式表示就是f(n)=f(n-1)+f(n-2)。这就是递归公式了。

再来看看终止条件,最后1个台阶就不需要再继续递归了,只有一种走法,就是f(1)=1。我们把这个放到递归公式里面看下,通过这个终止条件能否求出f(2),发现f(2)=f(1)+f(0),也就是仅知道f(1)是不能求出f(2)的,因此要么知道f(0)的值,或者直接将f(2)作为一个递归终止条件。f(0)表示0个台阶有几种走法,f(2)表示2个台阶有几种走法。明显,f(2)更容易理解一些。所以定为f(2)=2也是一个终止条件,表示最后2个台阶有两种走法,即一次跨1个台阶和一次跨2个台阶。有了f(1)和f(2),就能求出f(3),进而求出f(n)了。

转化成代码即是:

def walk(n):    if n == 1:  # 递归终止条件        return 1    if n == 2:  # 递归终止条件        return 2    return walk(n - 1) + walk(n - 2)  # 递归公式

13.3 递归可解决哪类问题

  • 原始问题的解可以分解为几个子问题的解

  • 原始问题和子问题,只有数据规模的不同,求解思路完全一样

  • 存在递归终止条件

13.4 递归存在的问题

  • 堆栈溢出

  • 重复计算

编写递归代码时,我们会遇到很多问题,比较常见的一个就是堆栈溢出,而堆栈溢出会造成系统性崩溃,后果会非常严重。什么是堆栈溢出呢?

函数调用会使用栈来保存临时变量。每调用一个函数,都会将临时变量封装为栈帧压入内存栈,等函数执行完成返回时,再出栈。系统栈或者虚拟机栈空间一般都不大。如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。

可以通过Pycharm工具查看调用栈的情况。在递归公式那行代码上添加断点,不断执行Step Over,可以看到Frames窗口中的栈信息会不断增加和减少,当调用一次函数会增加一帧,当调用返回后会减少一帧。最后返回第一层栈func.py。前面说的堆栈溢出的风险,体现在Frames窗口中的栈帧太多了。

那么,如何避免出现堆栈溢出呢?

通常可以在代码中限制递归调用的最大深度的方式来解决这个问题。比如Python语言,限制了递归深度,当递归深度过高,则会抛出:RecursionError: maximum recursion depth exceeded in comparison异常,防止系统性崩溃。

我们在代码中也可以自己设置递归的深度,比如限制n最大不能超过100,代码如下:

def walk(n):    if n == 1:        return 1    if n == 2:        return 2    if n > 100:        raise RecursionError("recursion depth exceede 100")    return walk(n - 1) + walk(n - 2)

除此之外,使用递归时还会出现重复计算的问题。什么意思?拿走台阶那个例子来说明。比如计算6个台阶的走法f(6),过程如下图:

从图中,我们可以直观地看到,想要计算 f(5),需要先计算 f(4) 和 f(3),而计算 f(4) 还需要计算 f(3),因此,f(3) 就被计算了很多次,这就是重复计算问题。

那么怎么解决这个问题?为了避免重复计算,我们可以通过字典保存已经求解过的 f(k)。当递归调用到 f(k) 时,先看下是否已经求解过了。如果是,则直接从字典中取值,不需要重复计算,这样就能避免刚讲的问题了。

修改下计算台阶走法的代码,解决重复计算的问题:

data = dict()  # 保存中间结果def walk(n):    if n == 1:        return 1    if n == 2:        return 2    if n > 100:        raise RecursionError("recursion depth exceed 100")    if n in data:  # 如果在中间结果中,则直接返回,不用进入递推公式再次计算        return data[n]    result = walk(n - 1) + walk(n - 2)  # 在递归公式前面增加个查找步骤    data[n] = result  # 将计算结果保存在中间结果data字典中    return resultprint(walk(6))

python 递归函数_连载|想用Python做自动化测试?递归函数相关推荐

  1. c++ 随机字符串_连载|想用Python做自动化测试?了解数值计算和随机数生成神器...

    " 本文掌握Python中的几种数值类型,以及算术运算.位运算.数值转换,再学习一个测试中常用来产生随机数的模块." 构建测试知识体系,欢迎关注 Python支持的数值类型有整型i ...

  2. append函数_连载|想用Python做自动化测试?函数的参数传递机制及变量作用域

    " 这一节有点难.看不懂没关系.继续往后学,回头再来看." 10.6 函数参数传递的机制 10.6.1 值传递与引用传递 编程语言的参数传递机制通常有两种: 值传递 拷贝参数的值, ...

  3. python生活中哪些运用_【想把python运用在实际生活中?那么python查询价格方法可以帮助你】- 环球网校...

    [摘要]通过本次课程可以让python学员了解一下python查询价格方法,对代码编程有个感性的认知.也好让大家能够理性选择,不要盲目跟从,选择适合自己当前阶段的学习内容,循序渐进,以兴趣自我探索为向 ...

  4. 想学python看什么书-想学习Python做数据分析,应该看哪些书?

    一.Python编程 /> 本书是一本针对所有层次的Python 读者而作的Python 入门书.全书分两部分:第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib.N ...

  5. 3 x 10的python表达式_这道数学题用PYTHON编程语言怎么写? 编程语言python是用

    我觉着,这个应该这样解决比较符合计算机解题思路. 下面的回答的,思考的东西太多. # -*- coding: utf-8 -*- __author__ = 'lpe234' __date__ = '2 ...

  6. pycharm是不是python编程_使用PyCharm进行python开发的简介

    使用PyCharm进行python开发的简介 这个是很常见的一个问题,我想学习python,用什么编辑器呢? eclipse+pydev? IDLE? vim? 每个人有自己的习惯,可能是自己琢磨的, ...

  7. excel和python建模_利用Excel学习Python:准备篇

    写在前面 这个系列我们要利用Excel的知识,学会用python进行数据分析,如果你精通Excel想要用python提高数据分析效率,那么这个系列你来对了,如果你已经是python大神,想要建模/算法 ...

  8. python图像分类_用于实现用python和django编写的图像分类的Keras UI

    KerasUI是一种可视化工具,可以在图像分类中轻松训练模型,并允许将模型作为服务使用,只需调用API. https://github.com/zeppaman/KerasUI 主要特点: 用oaut ...

  9. 计算机导论python知识点_如何系统地自学 Python?

    我是自学的Python.从对Python一无所知,到在博客上写Python相关的系列文章(Python快速教程),前后有将近三年的时间.期间有不少门槛,但也充满乐趣.乐趣是自学的最大动力.Python ...

最新文章

  1. 【java】第二十二节课(HashSet)
  2. Android中windowTranslucentStatus与windowTranslucentNavigation的一些设置(转)
  3. Centos中提示You have new mail in /var/spool/mail/root 解决
  4. HttpClient使用方法(包括POST文件)
  5. mfc程序转化为qt_智慧虎超:小程序如何为珠宝行业助力?低频商品的高频转化你懂吗...
  6. docker WARNING: IPv4 forwarding is disabled. 解决方法
  7. 最新的INTEL FPGA时序分析资料
  8. PHP从零开始--错误处理函数
  9. python切割图像,使用Python图像库将一个图像切割成多个图像
  10. 第六十八期:程序员与医生
  11. 自动驾驶路径轨迹规划(三阶曲线spline)
  12. python程序gpu运行时间表_python gpu任务及时调度
  13. 德勤发布2021中国成长型AI企业研究报告:迈向巅峰之路
  14. 适合程序员的健身方法(转)
  15. 浅谈FMA与SMA(test)
  16. 开关电源入门1-基本原理
  17. 有关嵌入式、单片机、51单片机、STM32、的一些概念详解
  18. linux将汇编转为机器码,如何将汇编语言转化为机器码
  19. 在几何画板中如何制作圆柱的侧面展开动画_如何用几何画板做三棱柱的侧面展开动画...
  20. 国外名校在线学位申请

热门文章

  1. [云炬python3玩转机器学习笔记] 3-2 Jupter Notebook魔法命令
  2. TypeError: 'function' object is not subscriptable
  3. python瀑布图怎么做_教你用Python创建瀑布图
  4. C#中代理的简单应用
  5. syslinux引导GRUB4DOS
  6. golang的GUI库,使用go-fyne设计一个代办事项APP
  7. XCTF-Reverse:open-source
  8. thymeleaf入门简介
  9. 减少if else的使用
  10. 2020-10-29