首页 > 编程知识 正文

递归函数简单实例,python一个递归函数必须有

时间:2023-05-03 15:00:31 阅读:22515 作者:3301

递归函数

在函数内部,可以调用其他函数。 如果一个函数在内部调用自己,则该函数是递归函数。

递归函数的特性:

需要明确的终止条件。每次进入更深的递归时,问题的规模与上一次递归相比必须减少的相邻两次重复之间存在密切联系,上一次必须为下一次准备(通常上一次输出为下一次输入)。 递归没有效率。 过多的递归层次会导致堆栈溢出。 (在计算机上,函数调用通过一种名为堆栈的数据结构实现,每次进入一个函数调用时,都会向堆栈中添加堆栈帧,每次函数返回时,堆栈都会减少一个堆栈帧。 堆栈的大小不是无限的,因此递归调用的次数过多会导致堆栈溢出。 (举个简单的例子。 计算从1到100的加法和。 用循环和递归两种方法实现

#循环方式defsum_cycle(n ) : sum=0 for i in range(1) 1,n 1 ) :sum=Iprint(sum(#递归方式def sum_recu(n ) n ) 33333650

5050

5050

递归函数的优点是定义简单,逻辑清晰。 理论上,所有递归函数都可以写循环的方式,但循环的逻辑不如递归清晰。

*要使用递归函数,必须防止堆栈溢出。 在计算机上,函数调用通过一种名为堆栈的数据结构实现,每次进入函数调用时都会向堆栈中添加堆栈帧,每次函数返回时堆栈都会减少一个堆栈帧。 堆栈的大小不是无限的,所以递归调用的次数太多,堆栈会溢出。

将以上递归和函数的参数更改为10000会导致堆栈溢出!

recursion error : maximumrecursiondepthexceededincomparison

*解决递归调用堆栈溢出的方法通过尾部递归进行优化,也可以将循环视为特殊的尾部递归函数,因为实际上尾部递归和循环的效果相同。

一般递归

defnormal_recursion(n ) : IFN==1: return1else : return normal _ recursion (n-1 )运行:

normal_recursion(5)5normal _ recursion (4) 54 normal _ recursion (3) 54 normal _ recursion (2) 542 normal _ recu recursion 随着递归深度的增加,创建的堆栈将增加到爆堆栈:boom:

3358 www.Sina.com/http://www.open-open.com/lib/view/open 148049463229.html http://www.Sina.com /

尾递归(各级调用直接返回函数返回值并更新调用栈,而无需创建新的调用栈。 在时间和空间上优化常见递归,就像迭代实现一样!

deftail_recursion(n,total=0) : IFN==0: returntotalelse 3360 return tail _ recursion (n-1,total n )运行:

tail_recursion (5(5(5) tail _ recursion ) 4,5 ) tail _ recursion (3,9 ) tail _ recursion (2,12 ) ttd

诶,所以呢? 你还觉得不够吗. 谁说不需要用尾部递归调用来创建新的堆栈呢?

还是到底层看看吧

inttail_recursion(intn,int total ) if ) n==0) { return total; }else{returntail_recursion(n-1,total n ); }intmain(void ) { int total=0,n=4; tail_recursion(n,total; 返回0; } 尾递归基于函数的尾调用

$ gcc-stail _ recursion.c-o normal _ recursion.s

$ gcc -S -O2 tail_recursi

on.c -o tail_recursion.S gcc开启尾递归优化

对比反汇编代码如下(AT&T语法)

可以看到, 开启尾递归优化前, 使用call调用函数, 创建了新的调用栈(LBB0_3);

而开启尾递归优化后, 就没有新的调用栈生成了, 而是直接pop

bp指向的 _tail_recursion 函数的地址(pushq %rbp)然后返回,

仍旧用的是同一个调用栈!

存在的问题

虽然尾递归优化很好, 但python 不支持尾递归,递归深度超过1000时会报错

一个牛人想出的解决办法

实现一个 tail_call_optimized 装饰器

#!/usr/lhzdxmg/env python2.4# This program shows off a python decorator(# which implements tail call optimization. It# does this by throwing an exception if it is# it's own grandparent, and catching such# exceptions to recall the stack.import sysclass TailRecurseException: def __init__(self, args, kwargs): self.args = args self.kwargs = kwargsdef tail_call_optimized(g): """ This function decorates a function with tail call optimization. It does this by throwing an exception if it is it's own grandparent, and catching such exceptions to fake the tail call optimization. This function fails if the decorated function recurses in a non-tail context. """ def func(*args, **kwargs): f = sys._getframe() # 为什么是grandparent, 函数默认的第一层递归是父调用, # 对于尾递归, 不希望产生新的函数调用(即:wnddh调用), # 所以这里抛出异常, 拿到参数, 退出被修饰函数的递归调用栈!(后面有动图分析) if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code: # 抛出异常 raise TailRecurseException(args, kwargs) else: while 1: try: return g(*args, **kwargs) except TailRecurseException, e: # 捕获异常, 拿到参数, 退出被修饰函数的递归调用栈 args = e.args kwargs = e.kwargs func.__doc__ = g.__doc__ return func@tail_call_optimizeddef factorial(n, acc=1): "calculate a factorial" if n == 0: return acc return factorial(n-1, n*acc)print factorial(10000)

为了更清晰的展示开启尾递归优化前、后调用栈的变化和tail_call_optimized装饰器抛异常退出递归调用栈的作用, 我这里利用 pudb调试工具 做了动图 <br/>

开启尾递归优化前的调用栈

开启尾递归优化后(tail_call_optimized装饰器)的调用栈

通过pudb右边栏的stack, 可以很清晰的看到调用栈的变化.

因为尾递归没有调用栈的嵌套, 所以Python也不会报 RuntimeError: maximum recursion depth exceeded 错误了!

这里解释一下 sys._getframe() 函数:

sys._getframe([depth]):Return a frame object from the call stack.If optional integer depth is given, return the frame object that many calls below the top of the stack.If that is deeper than the call stack, ValueEfror is raised. The default for depth is zero,returning the frame at the top of the call stack.即返回depth深度调用的栈帧对象.import sysdef get_cur_info(): print sys._getframe().f_code.co_filename # 当前文件名 print sys._getframe().f_code.co_name # 当前函数名 print sys._getframe().f_lineno # 当前行号 print sys._getframe().f_back # 调用者的帧

补充

二分法查找大家应该听说过;就是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的找出中间值,用中间值对比你需要找的实际值;若中间值大,则继续找左边;若中间值小,则继续找右边;可以看出二分法就是不断重复此上过程,所以就可以通过递归方式来实现二分法查找了!

#The lhzdxmgary search functiondef Binary_Search(data_source,find_n): #判断列表长度是否大于1,小于1就是一个值 if len(data_source) >= 1: #获取列表中间索引;奇数长度列表长度除以2会得到小数,通过int将转换整型 mid = int(len(data_source)/2) #判断查找值是否超出最大值 if find_n > data_source[-1]: print('{}查找值不存在!'.format(find_n)) exit() #判断查找值是否超出最小值 elif find_n < data_source[0]: print('{}查找值不存在!'.format(find_n)) exit() #判断列表中间值是否大于查找值 if data_source[mid] > find_n: print('查找值在 {} 左边'.format(data_source[mid])) #调用自己,并将中间值左边所有元素做参数 Binary_Search(data_source[:mid],find_n) #判断列表中间值是否小于查找值 elif data_source[mid] < find_n: #print('查找值在 {} 右边'.format(data_source[mid])) #调用自己,并将中间值右边所有元素做参数 Binary_Search(data_source[mid:],find_n) else: #找到查找值 print('找到查找值',data_source[mid]) else: #特殊情况,返回查找不到 print('{}查找值不存在!'.format(find_n)) Data = [22,12,41,99,101,323,1009,232,887,97]#列表从小到大排序Data.sort() #查找323 Binary_Search(Data,323) 执行结果:找到查找值 323

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。