Python3-递归函数

什么是递归?

递归,就是函数在运行的过程中调用自己。

代码示例

def recursion(n):

    print(n)
    recursion(n+1)

recursion(1)  

出现的效果就是,这个函数在不断的调用自己,每次调用就n+1,相当于循环了。

可是为何执行了900多次就出错了呢?还说超过了最大递归深度限制,为什么要限制呢?

通俗来讲,是因为每个函数在调用自己的时候 还没有退出,占内存,多了肯定会导致内存崩溃。

本质上讲呢,在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

递归的特点

让我们通过现象来看本质, 下面是是用递归写的,让10不断除以2,直到0为止。

打印的是1, 然后最里层的函数就结束了,结束后会返回到之前调用它的位置。即上一层,上一层打印的是2,再就是5,再就是10,即最外层函数,然后结束,总结,这个递归就是一层层进去,还要一层层出来。

通过上面的例子,我们可以总结递归几个特点:

  1. 必须有一个明确的结束条件,要不就会变成死循环了,最终撑爆系统
  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  3. 递归执行效率不高,递归层次过多会导致栈溢出

递归有什么用呢?

可以用于解决很多算法问题,把复杂的问题分成一个个小问题,一一解决。

比如求斐波那契数列、汉诺塔、多级评论树、二分查找、求阶乘等。用递归求斐波那契数列、汉诺塔 对初学者来讲可能理解起来不太容易,所以我们用阶乘和二分查找来给大家演示一下。

求阶乘

任何大于1的自然数n阶乘表示方法: 
n!=1×2×3×……×n 
或 
n!=n×(n-1)!

即举例:4! = 4x3x2x1 = 24

用递归代码来实现

def factorial(n):

    if n == 0: #是0的时候,就运算完了
        return 1
    return n * factorial(n-1) # 每次递归相乘,n值都较之前小1

d = factorial(4)
print(d)

2分查找

在一个已排序的数组data_set中,使用二分查找n,假如这个数组的范围是[low...high],我们要的n就在这个范围里。查找的方法是拿low到high的正中间的值,我们假设是mid,来跟n相比,如果mid>n,说明我们要查找的n在前数组data_set的前半部,否则就在后半部。无论是在前半部还是后半部,将那部分再次折半查找,重复这个过程,知道查找到n值所在的地方。

data_set = list(range(101))

def b_search(n,low,high,d):

    mid = int((low+high)/2) # 找到列表中间的值
    if low == high:
        print("not find")
        return
    if d[mid] > n: # 列表中间值>n, 代数要找的数据在左边
        print("go left:",low,high,d[mid])
        b_search(n,low,mid,d) # 去左边找
    elif d[mid] < n: # 代数要找的数据在左边
        print("go right:",low,high,d[mid])
        b_search(n,mid+1,high,d) # 去右边找
    else:
        print("find it ", d[mid])

b_search(188, 0,len(data_set),data_set)
go right: 0 101 50
go right: 51 101 76
go right: 77 101 89
go right: 90 101 95
go right: 96 101 98
go right: 99 101 100
not find

最多将会操作7次,其实因为每一次我们都抛掉当前确定的区间的一半的区间作为不可能解部分,那么相当于求最多操作次数,就是在区间内,最多将有多少个一半可以抛去、那么就是将100一直除以2,直到不能除为止。

那么这个运算过程,其实就是相当于求了一个log2(100)≈7。

补充:

在讲特性时,我们说递归效率不高,因为每递归一次,就多了一层栈,递归次数太多还会导致栈溢出,这也是为什么python会默认限制递归次数的原因。但有一种方式是可以实现递归过程中不产生多层栈的,即尾递归,

尾递归

在函数最尾部有return,return值是递归形式调用,且返回值与上一层函数无任何依赖。

尾递归例子

def calc(n):
    print(n - 1)
    if n > -50:
        return calc(n-1)

我们之前求的阶乘是尾递归么?

def factorial(n):

    if n == 0: #是0的时候,就运算完了
        return 1
    return n * factorial(n-1) # 每次递归相乘,n值都较之前小1

d = factorial(4)
print(d)

上面的这种递归计算最终的return操作是乘法操作。所以不是尾递归。因为每个活跃期的返回值都依赖于用n乘以下一个活跃期的返回值,因此每次调用产生的栈帧将不得不保存在栈上直到下一个子调用的返回值确定。

原文地址:https://www.cnblogs.com/Xuuuuuu/p/10165293.html

时间: 2024-10-10 01:37:39

Python3-递归函数的相关文章

python3学习之递归函数

##递归函数 #自己调用自己 def t(a):     if a == 1:         return 1     return a + t(a-1) b = t(7) print(b) #计算1+2+3+4+5+6+7 的和

Python3学习笔记(2)——递归函数

如果一个函数在内部调用自身本身,这个函数就是递归函数. 递归函数的优点是定义简单,逻辑清晰.理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰. 使用递归函数需要注意防止栈溢出.在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧.由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出.可以试试fact(1000) 解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,

python3.5修炼手册12

函数 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码. 定义函数 python支持自定义函数,即由我们自己定义一个实现某个功能的函数. 自定义函数规则: ①函数代码块以def关键字开头,后接函数标识符名称和圆括号"()". ②所有传入的参数和自定义变量都必须放在圆括号中,可以在圆括号中定义参数. ③函数的第一行语句可以选择性的使用文档字符串,用于存放函数说明. ④函数内容以冒号开始,并且要缩进. ⑤return[表达式]结束函数,选择性的返回一个值给调用方.不带表达式的r

递归函数之阶乘和字符串反转-基于R和Python

Python课第五周开始讲函数了.递归函数.递归在python中不能超过900多层,否则报错内存溢出什么的.同样在R中递归太深也会报错,阈值和python中大概一样,900多次就报错了. error message: 错误: 评估嵌套太深:无穷递归/ options(expressions=)?收捲时出错: 评估嵌套太深:无穷递归/ options(expressions=)? 基于Python # 递归函数 阶乘 def fact(n): if n==0: return 1 else: ret

机器学习实战python3 决策树ID3

代码及数据:https://github.com/zle1992/MachineLearningInAction 决策树 优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据. 缺点:可能会产生过度匹配问题. 适用数据类型:数值型和标称型. 创建分支的伪代码函数createBranch()如下所示:检测数据集中的每个子项是否属于同一分类:if so return 类标签; Else 寻找划分数据集的最好特征 划分数据集 创建分支节点 for 每个划分的子集 调用函

Python3基础——递归

递归函数 如果一个函数在内部调用自身本身,这个函数就是递归函数. 递归函数的优点是定义简单,逻辑清晰.理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰. 使用递归函数需要注意防止栈溢出.在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧.由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出.python3默 认递归的深度是100,如果想要更改递归深度,可以导入sys模块,设置递归深度最

Python基础篇【第八篇】:剖析递归函数

递归函数 如果函数中包含了对其自身的调用,该函数就是递归函数! 先介绍一下斐波那契数列:斐波那契数列成为黄金分割数列,表现形式0.1.1.2.3.5.8.13.21.34........ 可以看出前两个的数的和等于第三个数0 + 1 = 1,1 + 1 = 2 , 1 + 2 = 3 ...... 通过斐波那契数列剖析递归函数: 1 #!/usr/bin/env python3 2 #通过斐波那契数列详细剖析递归函数 3 #0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,

8.5高阶函数、递归函数和内置函数

高阶函数.递归函数和内置函数 高阶函数和递归函数 #函数名可以进行赋值,可以作为函数参数,可以作为返回值 #高阶函数:允许导入函数作为参数导入或者返回值为函数 def f(n): return n*n def fun(a,b,fun1): an1=fun1(a)+fun1(b) return (an1) print(fun(1,2,f)) def sqrt(n): return n*n def function(a,b,function1): answer=function1(a)+functi

python3学习第三周

1.列表,元组操作 列表是通过下标(索引)对元素进行增删该查的. 列表中可以嵌套列表,元组. 列表是有序的. 元组是只读的,不能更改. 2.字符串操作 字符串是不能更改的 3.字典操作 字典是通过key对value进行增删改查的. 字典可以嵌套列表,字典. 字典是无序的. 4.集合操作 集合是一个无序的,不重复的数据组合,它的主要作用是 a.去重:把一个列表变成集合,就自动去重了. b.关系测试:测试两组数据之间的交集.差集.并集等关系. a=set([1,2,3,4,5,6])   #创建一个

python3自动化学习04

概要:迭代器.yield生成器.装饰器.递归函数.简单的算法(二分查找,二维数组).正则表达式基础 一.迭代器 迭代器是访问集合元素的一种方式,迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束. 迭代器只能往前不能后退.迭代器的一大优点就是不要求事先准备好整个迭代的过程中所有的元素,迭代器仅仅是在迭代到某个元素才计算该元素,而在这之前或之后,元素可以不存在或者销毁,这个特点使它特别适用于遍历一些巨大或无限的集合. 特点: 1.访问者不需要关心迭代器内部的结构,仅仅需要通过 __n