轻松搞懂Python递归函数的原理与应用
递归: 在函数的定义中,函数内部的语句调用函数本身。
1、递归的原理
学习任何计算机语言过程中,“递归”一直是所有人心中的疼。不知你是否听过这个冷笑话:“一个面包,走着走着饿了,于是就把自己吃了”。
呵呵。

常理推断,特别是解释型语言,当程序执行函数内部的语句时,这个函数还没有定义完,没定义完怎么可以调用本身呢。
但实质上,当你执行函数内部的语句时,一定有函数外部的语句调用了这个函数,此时该函数的所有代码和语句,已经在内存中形成了逻辑,这就是递归函数的原理。
在Python当中很重要的就是递归的使用。
2、递归的玩法
牛叔语录:人类创造出的任何复杂的概念,都是为了让事情变得更简单一些。
递归是为了更好的解决,“自已定义自己”的数学或是哲学难题。
用好递归,有2个、2个、2个(重要的事情说3遍)要点,必须要同时注意,下面请各位评委看我的表演。
(1) 认清自己
认清自己是递归玩法的核心。与处理任何事情一样,首先要问的就是:如何准确地定义好“当前要做的事情”。处理好这个问题,我们就已经成功了一半。
此处我们拿数学函数为例, 因为数学函数的定义有公式,显而易见,它的意义就是根据变量来计算出结果,最容易被小白理解,栗子如下:
假设数学函数:
f(n) = (f(n-1) + 1)*2 ,告诉你f(1)=1,问f(10)是多少?
我们直接把这个f(n)定义成函数,并且计算f(10) 如下:
def f(n):
if n==1:
return 1
else:
return (f(n-1)+1)*2 print(f(10))
结果是1534,运行正确。恭喜你这是一道著名的数学难题,你竟然轻松解决了,原题这样:
猴子有一堆桃子,每天吃前一天剩下的一半多1个,昨天吃完发现剩了1个,那么前10天它剩下多少个桃子?
我们公式中的n就表示前n天,公式结果就表示剩下了多少桃子,昨天的桃子y总比今天的x有这样的关系:x = y/2 -1 所以:y=(x+1)*2,这个就是上面的公式。
在写本递归函数时,语句基本上照抄数学公式,我们不知道解题过程就能求出答案,真是太神奇了!只要定义好,递归跑不了!慢着,别下结论,我们再看看如下的点。
(2)把握好方向
与上面的“猴子摘桃”同一个问题,原题我们改一下说法,问:
猴子第1天摘了一堆桃子吃了一半又多一个,第2天吃了剩下的一半又多一个,...,第10天早上时发现只有1个桃子了。问第1天摘了多少?这回我们把n设为第n天(而不是前n天)
与上面函数f类似,后项也是根据前项值来确定,为区别我们使用g()代表该函数:
g(n) = g(n-1)/2 -1 ,告诉你 g(10)=1,问g(1) 是多少?
牛叔说了,“只要定义好,递归跑不了”,我们先不管三七二十一,照抄上面的数学公式,“迅速而又准确”地把g(n)函数用如下的代码造了出来,如下:
def g(n):
if n==10:
return 1
else:
return g(n-1)/2 - 1 print(g(1))
运行后却出现了如下的错误,提示递归溢出!

这是怎么回事呢?因为我们还有第2个点,您没有注意!
这个点就是 “要想递归不出错,开车方向不能错”,这个方向是指: 程序求解的方向必须和定义的方向相同,
此处我们要根据n=10的结果来求n=1,方向是:后项(10)->前项(1),而程序中翻译的公式是 前项(n-1)->后项(n),根据公式写的程序,也只能完成公式的求解顺序,即:n从小到大的推导,
所以此时如果求g(100),结果是这样的,完全没有问题(但没有任何意义):
-2.0
所以此处要通过我们的智慧把这个公式,“颠倒”过来变成:
g(n-1) = (g(n)+1) * 2 => g(n) = (g(n+1)+1) * 2
这样根据公式写的代码,就可以完成从后项(n+1)推导到前项(n)的功能了!
如果不理解,小学二年级的代数再看一遍,具体程序如下:
def g(n):
if n==10:
return 1
else:
return 2*(g(n+1)+1) print(g(1))
终于得出了正确的结果,1534。答案和第1题一样!猴子终于满意了,10天的口粮有着落了。
3、分解质因数练习
小学生专用名词,别给吓住了,其实就是把数字分解成不能再分解的乘法,比如:8=2*2*2, 10 = 2*5,而不是 8 = 2 * 4 这种可以再分解的。
此递归问题,是编程竞赛常客,拿来练手比较合适。如下Python代码,对于本问题的解答充分说明了递归的方便之处,以分解了980这个整数为示例:
def defactor(N): #定义一个函数名称为defactor,意义是返回N的所有因子
for i in range(2,N): #从2开始试试
if N%i ==0: #如果试到i是N的因子的话,就返回i的所有因子和N/i的所有因子 的列表
return defactor(i)+defactor(int(N/i))
else:#如果没有试到就说明这个N是一个质数,就直接包含它的 列表
return [N] print(defactor(980))
运行的答案是:
[2, 2, 5, 7, 7]
Bingo,在这里函数defactor(N)
(1)定义: 求出参数N所有因子的数组,
(2)方向:整数 -> 最小的质数
首先,理解else:部分(代码中的第5,6两行),
这是函数的,把方向的部分,小牛叔称他为:结果产生部分,它能确保程序不会跑偏的代码,如果传入的数字N没有可以被整除的数字,即循环到头了,才会执行这个部分,每一个质数因子都会从这个部分产生,因为最终的结果只能是质数。下图树中的所有叶节点(没有子节点的叶子:2,2,5,7,7),就是从这个部分产生的,当执行到这个部分时,程序不会再往向下递归,所以不会产生下层的树型结构。
再理解,递归的部分(2,3,4行)
通过循环来试因子,如果试到i是N的因子(即N可以被i整除)的话,就返回i的所有因子和 N/i的所有因子 组成的列表,因此下图中所有从上到下的两个箭头对,就代表着这两个递归调用。
通过这个树形解题的过程,您会更加明白!

在整个程序递归执行时,看起来是从顶980开始向下求解执行,而求解的过程中结果却是从最下层的7,7开始向上汇集。
轻松搞懂Python递归函数的原理与应用的更多相关文章
- 一文轻松搞懂redis集群原理及搭建与使用
今天早上由于zookeeper和redis集群不在同一虚拟机导致出了点很小错误(人为),所以这里总结一下redis集群的搭建以便日后所需同时也希望能对你有所帮助. 笔主这里使用的是Centos7.如果 ...
- Redis | 一文轻松搞懂redis集群原理及搭建与使用
转载:https://juejin.im/post/5ad54d76f265da23970759d3 作者:SnailClimb 这里总结一下redis集群的搭建以便日后所需同时也希望能对你有所帮助. ...
- Python高级特性: 12步轻松搞定Python装饰器
12步轻松搞定Python装饰器 通过 Python 装饰器实现DRY(不重复代码)原则: http://python.jobbole.com/84151/ 基本上一开始很难搞定python的装 ...
- C++ 一篇搞懂多态的实现原理
虚函数和多态 01 虚函数 在类的定义中,前面有 virtual 关键字的成员函数称为虚函数: virtual 关键字只用在类定义里的函数声明中,写函数体时不用. class Base { virtu ...
- 转载 12步轻松搞定python装饰器
作者: TypingQuietly 原文链接: https://www.jianshu.com/p/d68c6da1587a 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定pyt ...
- 12步轻松搞定Python装饰器
译者:寒寻 译文:http://www.cnblogs.com/imshome/p/8327438.html 原文:https://dzone.com/articles/understanding-p ...
- 轻松搞懂Java中的自旋锁
前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...
- 一文搞懂Python迭代器和生成器
很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...
- 轻松搞懂WebService工作原理
用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...
随机推荐
- 消息队列rabbitmq的五种工作模式(go语言版本)
前言:如果你对rabbitmq基本概念都不懂,可以移步此篇博文查阅消息队列RabbitMQ 一.单发单收 二.工作队列Work Queue 三.发布/订阅 Publish/Subscribe 四.路由 ...
- Scala 学习(3)之「类——基本概念1」
类 小提示:可以通过:paste进入 Scala 的多行模式,输入对应的代码块之后,按ctrl + D退出多行模式,然后再调用刚才输入的函数或者方法进行测试 //定义类,包含 field 以及方法 c ...
- pip 下载源更换
Python博大精深之处在于丰富的库,而目前最方便的下载库的方法无疑是pip.但是由于国内的网络环境导致,好多库下载是在太慢了,原因大家都懂得.. 一.临时修改 使用pip的时候加入参数 -i pip ...
- cogs 1583. [POJ 3237] 树的维护 树链剖分套线段树
1583. [POJ 3237] 树的维护 ★★★★ 输入文件:maintaintree.in 输出文件:maintaintree.out 简单对比时间限制:5 s 内存限制:128 ...
- springcloud初次zuul超时报错com.netflix.zuul.exception.ZuulException:Forwarding error
报错如下 com.netflix.zuul.exception.ZuulException:Forwarding error Caused by: com.netflix.hystrix.except ...
- 【Flink】Flink作业调度流程分析
1. 概述 当向Flink集群提交用户作业时,从用户角度看,只需要作业处理逻辑正确,输出正确的结果即可:而不用关心作业何时被调度的,作业申请的资源又是如何被分配的以及作业何时会结束:但是了解作业在运行 ...
- 【java面试】框架篇之Spring
1.你如何理解Spring? 具体来说Spring是一个轻量级的容器,用于管理业务相关对象的.核心功能主要为:IOC,AOP,MVC. IOD:控制反转,将对象的创建过程交给容器,让容器管理对象的生命 ...
- python文件与输入输出
注:本文档是学习<Python核心编程(第二版)>时的整理. 1.文件对象 文件对象不仅可以用来访问普通的磁盘文件,也可以访问任何其他类型抽象层面上的"文件".一旦设置 ...
- 爬虫之 cookie , 验证码,模拟登陆,线程
需求文档的定制 糗事百科的段子内容和作者(xpath的管道符)名称进行爬取,然后存储到mysql中or文本 http://sc.chinaz.com/jianli/free.html爬取简历模板 HT ...
- mac电脑下使用fcrackzip破解zip压缩文件密码
fcrackzip简介 fcrackzip是一款专门破解zip类型压缩文件密码的工具,工具小巧方便.破解速度快,能使用字典和指定字符集破解,适用于linux.mac osx 系统 fcrackzip安 ...