大家好我是致力于让每个人都能够轻松学会编程的小梁,在这条路上任重道远,关注我,每天让您获取来自编程的乐趣。
关注公众号“轻松学编程”。了解更多。
今天就给大家分享一下关于使用递归函数求解一些数学问题时需要注意的事。

什么是递归

什么是递归: 递归是指一种通过重复将问题分解为同类的子问题而解决问题的方法,在python中间接或直接调用自身的函数被称为递归函数。

间接:
def func():
otherfunc() def otherfunc():
func() 直接:
def func():
func()

递归函数的优点定义简单,逻辑清晰,理论上所有的递归函数都可以写成循环的方式(这个已经被伟大的科学家证明了的),但是循环的逻辑不如递归清晰。

递归函数必须要有收敛条件和递归公式。

注意:使用递归函数需要注意防止栈溢出,在计算机中函数是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,就会增加一层栈帧,每当函数返回,栈就会减一层栈帧,栈的大小不是有限制的,所以当调用的次数过多的时候,会导致栈溢出。

使用递归比较占内存。

下面介绍一些可以用得到递归的场景。

1、递归求和

def my_sum(n):
'''
递归求和
1+2+3+...+n
:param n: int型
:return: int型
'''
# 收敛条件
if n == 1:
print('第1层递归结束,返回结果1给上一层')
return 1

# 递归公式
next_level = my_sum(n - 1)
ret = n + next_level
print('第{0}层递归结果是:{0} + {1} = {2}'.format(n, next_level, ret))
return ret


if __name__ == '__main__':
ret = my_sum(10)
print('递归结果', ret)
第1层递归结束,返回结果1给上一层
第2层递归结果是:2 + 1 = 3
第3层递归结果是:3 + 3 = 6
第4层递归结果是:4 + 6 = 10
第5层递归结果是:5 + 10 = 15
第6层递归结果是:6 + 15 = 21
第7层递归结果是:7 + 21 = 28
第8层递归结果是:8 + 28 = 36
第9层递归结果是:9 + 36 = 45
第10层递归结果是:10 + 45 = 55
递归结果 55

当发生函数调用时 需要做保存现场和恢复现场的工作。当遇到收敛条件后,递归才会正式结束,然后把结果1(即my_sum(1)=1)返回第二层,第二层得到第一层的结果才会计算2+my_sum(1)=2+1=3,第二层得到结果后会传给第三层,以此类推,到最后一层得到最终结果,整个递归结束。

保存现场和恢复现场的工作都是利用栈(stack)来实现的。

栈是一个FILO的结构 - 栈非常的快但是它很小。

python默认栈的层数为1000层,可以使用以下方法来增加层数(不推荐)

 import sys
# 设置递归层数最大值为9999层
sys.setrecursionlimit(9999)

这样的递归不好,因为递归使用的是栈,需要用栈来保护现场和恢复现场,很耗费资源,可以使用尾递归来解决这个问题,即不回溯,直接使用最后一次的结果作为最终的结果。

2、尾递归

import sys
# 设置递归层数最大值为100000层
sys.setrecursionlimit(100000)
'''
使用递归求和
'''


def my_sum(n, result=0):
'''
递归求和
1+2+3+...+n
:param n: int型
:param result: int型,上一层求得的结果,第一层递归时为0
:return: int型
'''
# 收敛条件

if n == 1:
print('第1层递归结束,直接返回计算结果{}'.format(result + 1))
return result + 1
# 递归公式
print('第{}层递归,当前求值结果是:{}'.format(n, result))
return my_sum(n - 1, result=result + n)


if __name__ == '__main__':
my_sum(10)
第10层递归,当前求值结果是:0
第9层递归,当前求值结果是:10
第8层递归,当前求值结果是:19
第7层递归,当前求值结果是:27
第6层递归,当前求值结果是:34
第5层递归,当前求值结果是:40
第4层递归,当前求值结果是:45
第3层递归,当前求值结果是:49
第2层递归,当前求值结果是:52
第1层递归结束,直接返回计算结果55

这样做有什么好处呢?第一,result代表上一次调用函数求得的结果,有了这个结果我就可以直接做运算,而不需要再去回溯函数获取上次调用函数的值。

3、递归求斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……

在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)。

斐波那契数列:
当n = 1, 2时 f(n) = 1
当n > 2时 f(n) = f(n - 1) + f(n - 2)
比如:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55...]
import sys
# 设置递归层数最大值为100000层
sys.setrecursionlimit(100000)


def fibonacci(n):
# 收敛条件
if n <= 2:
return 1
# 递归公式
return fibonacci(n - 1) + fibonacci(n - 2)


if __name__ == '__main__':
n = 12
print('当下标为{}时,对应的数值是{}'.format(n, fibonacci(n)))

当下标为12时,对应的数值是144
这样的递归是不好的,因为每求一层递归都要重新计算前面(n-1)层递归,开销很大,比如:

求f(9)
要先求f(8) + f(7)
而f(8)= f(7) + f(6)
f(7)=f(6) + f(5)

你可以测试一下,使用n=120,你会发现需要跑很久才会得出结果。

为了节省这部分重复的开销,可以使用动态规划来解决这个问题。

4、动态规划实现递归

**动态规划(**dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。

20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

其基本思想就是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。

我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

在python中的递归,可以使用字典来代替这个表。

'''
斐波那契数列:
当n = 1, 2时 f(n) = 1
当n > 2时 f(n) = f(n - 1) + f(n - 2)
比如:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55...]
'''
import sys

# 设置递归层数最大值为100000层
sys.setrecursionlimit(100000)


def fibonacci(n, temp={}):
# 收敛条件
if n <= 2:
return 1
# 首先先从字典中取值
try:
return temp[n]
except KeyError:
# 如果字典中没有就先把值存进字典
temp[n] = fibonacci(n - 1) + fibonacci(n - 2)
return temp[n]


if __name__ == '__main__':
# 使用动态规划后,不到1秒就得出结果了
n = 120
print('当下标为{}时,对应的数值是{}'.format(n, fibonacci(n)))

当下标为120时,对应的数值是5358359254990966640871840

使用动态规划解决:

一个小孩爬阶梯,一次有3种走法:一次走1个阶梯,一次走2个阶梯,一次走3个阶梯,问如果有10个阶梯总共有多少中走法?

import sys

# 递归层数为100000
sys.setrecursionlimit(100000)


def walk(steps, temp={}):
if steps < 0:
return 0
elif steps == 0:
return 1
try:
return temp[steps]
except KeyError:
temp[steps] = walk(steps - 1) + walk(steps - 2) + walk(steps - 3)
return temp[steps]


if __name__ == '__main__':
print('一共有{}种走法'.format(walk(10)))

一共有274种走法

5、使用装饰器测试递归函数消耗时间

'''
斐波那契数列:
当n = 1, 2时 f(n) = 1
当n > 2时 f(n) = f(n - 1) + f(n - 2)
比如:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55...]
'''
import sys
import time

# 设置递归层数最大值为100000层
sys.setrecursionlimit(100000)
from functools import wraps


def func_time(func):
# 使用@wraps后可以还原原来的函数
# 即可以使用func.__wrapped__()来去掉装饰器,直接执行原函数的功能
@wraps(func)
def wrapper(*arg, **kwargs):
# 第一层递归才输出时间
if kwargs['level'] == 0:
start = time.perf_counter()
result = func(*arg, **kwargs)
end = time.perf_counter()
print(f'执行时间:{end-start}s')
return result
else:
return func(*arg, **kwargs)

return wrapper


@func_time
def fibonacci(n, temp={}, *, level):
# 收敛条件
if n <= 2:
return 1
# 首先先从字典中取值
try:
return temp[n]
except KeyError:
level += 1
# 如果字典中没有就先把值存进字典
temp[n] = fibonacci(n - 1, level=level) + fibonacci(n - 2, level=level)
return temp[n]


def main():
n = 121
print(f'当下标为{n}时,对应的数值是{fibonacci(n, level=0)}')

fib_19 = fibonacci.__wrapped__(19, level=0)
fib_20 = fibonacci.__wrapped__(20, level=0)

print(f'黄金比例:{fib_19/fib_20}') ​
if __name__ == '__main__':
main()
执行时间:0.0009382s
当下标为121时,对应的数值是8670007398507948658051921
黄金比例:0.6180339985218034
黄金比例:0.6180339887498949

注意1:能用循环写的代码最好不要使用递归,因为递归有可能造成栈溢出。

注意2:如果用递归也尽量使用尾递归(只需要递归不需要回溯)和动态规划。

后记

【后记】为了让大家能够轻松学编程,我创建了一个公众号【轻松学编程】,里面有让你快速学会编程的文章,当然也有一些干货提高你的编程水平,也有一些编程项目适合做一些课程设计等课题。

也可加我微信【1257309054】,拉你进群,大家一起交流学习。
如果文章对您有帮助,请我喝杯咖啡吧!

公众号

关注我,我们一起成长~~

关于python递归函数,这样写就对了的更多相关文章

  1. Python 递归函数 详解

    Python 递归函数 详解   在函数内调用当前函数本身的函数就是递归函数   下面是一个递归函数的实例: 第一次接触递归函数的人,都会被它调用本身而搞得晕头转向,而且看上面的函数调用,得到的结果会 ...

  2. python递归函数(10)

    一个函数在函数体内部调用自己,这样的函数称为递归函数,递归的次数在python是有限制的,默认递归次数是997次,超过997次会报错:RecursionError. 一.递归函数案例 案例一:计算数字 ...

  3. python 递归函数

    在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. 举个例子,我们来计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示,可以看出: f ...

  4. python递归函数下不能正常使用yield

    # -*- coding:utf-8 -*- import os import time file_list = [] def findFile(path): listFile = os.listdi ...

  5. Python递归函数

    参考: https://pythonspot.com/recursion/ https://www.python-course.eu/recursive_functions.php 一.递归函数两大要 ...

  6. python—递归函数

    递归函数 定义:即在函数定义中自己调用自己 递归就是在过程或函数中自我调用 递归必须有递归出口,即递归结束条件 举个栗子-阶乘: def fact(n): if n == 1: return 1 re ...

  7. 16 Python 递归函数

    递归 1.什么是递归 recursion 递归 递归的定义——在一个函数里再调用这个函数本身 在一个函数里再调用这个函数本身,这种魔性的使用函数的方式就叫做递归. 递归的最大深度——997 一个函数在 ...

  8. Python递归函数和二分查找算法

    递归函数:在一个函数里在调用这个函数本身. 递归的最大深度:998 正如你们刚刚看到的,递归函数如果不受到外力的阻止会一直执行下去.但是我们之前已经说过关于函数调用的问题,每一次函数调用都会产生一个属 ...

  9. Python递归函数,二分查找算法

    目录 一.初始递归 二.递归示例讲解 二分查找算法 一.初始递归 递归函数:在一个函数里在调用这个函数本身. 递归的最大深度:998 正如你们刚刚看到的,递归函数如果不受到外力的阻止会一直执行下去.但 ...

随机推荐

  1. 微服务通信之feign的注册、发现过程

    前言 feign 是目前微服务间通信的主流方式,是springCloud中一个非常重要的组件.他涉及到了负载均衡.限流等组件.真正意义上掌握了feign可以说就掌握了微服务. 一.feign的使用 f ...

  2. shiro认证流程源码分析--练气初期

    写在前面 在上一篇文章当中,我们通过一个简单的例子,简单地认识了一下shiro.在这篇文章当中,我们将通过阅读源码的方式了解shiro的认证流程. 建议大家边读文章边动手调试代码,这样效果会更好. 认 ...

  3. JDBC Java 连接 MySQL 数据库

    MySQL 版本:Server version: 5.7.17-log MySQL Community Server (GPL) 用于测试的 MySQL 数据库:game 查看数据库中的表 mysql ...

  4. Java 客户端操作 FastDFS 实现文件上传下载替换删除

    FastDFS 的作者余庆先生已经为我们开发好了 Java 对应的 SDK.这里需要解释一下:作者余庆并没有及时更新最新的 Java SDK 至 Maven 中央仓库,目前中央仓库最新版仍旧是 1.2 ...

  5. golang拾遗:指针和接口

    这是本系列的第一篇文章,golang拾遗主要是用来记录一些遗忘了的.平时从没注意过的golang相关知识.想做本系列的契机其实是因为疫情闲着在家无聊,网上冲浪的时候发现了zhuihu上的go语言爱好者 ...

  6. ActiveMQ详细入门教程系列(一)

    一.什么是消息中间件 两个系统或两个客户端之间进行消息传送,利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成.通过提供消息传递和消息排队模型,它可以在分布式环境下 ...

  7. SpringSecurity原理

    一.认证的两种方式的介绍 1. 基于Session的认证方式 在之前的单体架构时代,我们认证成功之后都会将信息存入到Session中,然后响应给客户端的是对应的Session中数据的key,客户端会将 ...

  8. linux 已放弃(吐核) (core dumped) 问题分析

    在运行自己写的 C 多线程程序是,出现:已放弃(吐核)  问题. 出现这种问题一般是下面这几种情况: 1.内存越界 2.使用的非线程安全的函数 3.全局数据未加锁保护 4.非法指针 5.堆栈溢出 也就 ...

  9. 极简 Node.js 入门 - 5.1 创建 HTTP 服务器

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  10. 2014年 实验三 B2B模拟实验(一)

    [实验目的] ⑴.熟悉企业网络银行和电子证书的应用 ⑵.通过B2B模拟实验掌握B2B的交易过程 [实验条件] ⑴.个人计算机一台 ⑵.计算机通过局域网形式接入互联网 (3).电子商务模拟实验室软件包. ...