递归: 在函数的定义中,函数内部的语句调用函数本身。

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递归函数的原理与应用的更多相关文章

  1. 一文轻松搞懂redis集群原理及搭建与使用

    今天早上由于zookeeper和redis集群不在同一虚拟机导致出了点很小错误(人为),所以这里总结一下redis集群的搭建以便日后所需同时也希望能对你有所帮助. 笔主这里使用的是Centos7.如果 ...

  2. Redis | 一文轻松搞懂redis集群原理及搭建与使用

    转载:https://juejin.im/post/5ad54d76f265da23970759d3 作者:SnailClimb 这里总结一下redis集群的搭建以便日后所需同时也希望能对你有所帮助. ...

  3. Python高级特性: 12步轻松搞定Python装饰器

    12步轻松搞定Python装饰器 通过 Python 装饰器实现DRY(不重复代码)原则:  http://python.jobbole.com/84151/   基本上一开始很难搞定python的装 ...

  4. C++ 一篇搞懂多态的实现原理

    虚函数和多态 01 虚函数 在类的定义中,前面有 virtual 关键字的成员函数称为虚函数: virtual 关键字只用在类定义里的函数声明中,写函数体时不用. class Base { virtu ...

  5. 转载 12步轻松搞定python装饰器

    作者: TypingQuietly 原文链接: https://www.jianshu.com/p/d68c6da1587a 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定pyt ...

  6. 12步轻松搞定Python装饰器

    译者:寒寻 译文:http://www.cnblogs.com/imshome/p/8327438.html 原文:https://dzone.com/articles/understanding-p ...

  7. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...

  8. 一文搞懂Python迭代器和生成器

    很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...

  9. 轻松搞懂WebService工作原理

    用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...

随机推荐

  1. Vmware下Ubuntu 14.04静态IP地址的设置方法

    一.环境 宿主机 Win 8.1 虚拟机工具 VMware 10.0 虚拟主机系统 Ubuntu 14.04   二.说明 这里需要注意的是:VMware对于VMnet8采用如下规则(192.168. ...

  2. ArcEngine 数据编辑(IWorkspaceFactory)

    数据编辑做过很多次,没怎么出现问题,今天出现了问题,浪费了大半天,记录一下. 问题:修改Featrue的属性,修改后停止编辑,但是没有提示是否保存修改 原因:在编辑数据的时候没有加StartEditO ...

  3. 记录一次mybatis genertor使用以及mapper扫描遇见的问题

    本记录适用初次接触mybatis,大神忽略... 整体上分两个部分: 1.使用mybatis genertor自动生成代码 2.mapper的扫描 1.使用mybatis genertor自动生成代码 ...

  4. Java BIO NIO 与 AIO

    回顾 上一章我们介绍了操作系统层面的 IO 模型. 阻塞 IO 模型. 非阻塞 IO 模型. IO 复用模型. 信号驱动 IO 模型(用的不多,知道个概念就行). 异步 IO 模型. 并且介绍了 IO ...

  5. SqlServer分页存储过程(多表查询,多条件排序),Repeater控件呈现数据以及分页

        存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出 ...

  6. 支撑京东小程序的开发框架 「Taro」

    Taro 简介 现在支持小程序的平台太多了,例如: 微信小程序 QQ小程序 支付宝小程序 百度小程序 字节跳动小程序 针对他们都各自开发一套的话开发成本就太高了. 如果写一套代码,就能开发出适配这么多 ...

  7. 浏览器警告Failed to decode downloaded font和OTS parsing error: Failed to convert *** font to ***

    昨晚,在做一个自己感兴趣的东西时,发现浏览器报警告,Failed to decode downloaded font以及OTS parsing error: Failed to convert *** ...

  8. L1和L2:损失函数和正则化

    作为损失函数 L1范数损失函数 L1范数损失函数,也被称之为最小绝对值误差.总的来说,它把目标值$Y_i$与估计值$f(x_i)$的绝对差值的总和最小化. $$S=\sum_{i=1}^n|Y_i-f ...

  9. 正斜杠(" / ")和反斜杠(" \ ")的区别

    反斜杠“\”是电脑出现了之后为了表示程序设计里的特殊含义才发明的专用标点.所以除了程序设计领域外,任何地方都不应该使用反斜杠. 如何区分正反斜杠 英语:"/" 英文是forward ...

  10. Nginx配置及负载均衡

    转载:http://www.cnblogs.com/jingmoxukong/p/5945200.html nginx简易教程   目录 Nginx  概述  安装与使用  nginx 配置实战  参 ...