最大递归深度是多少,如何增加它?

2024-11-19 08:38:00
admin
原创
10
摘要:问题描述:我在这里有这个尾部递归函数:def recursive_function(n, sum): if n < 1: return sum else: return recursive_function(n-1, sum+n) c = 998 pri...

问题描述:

我在这里有这个尾部递归函数:

def recursive_function(n, sum):
    if n < 1:
        return sum
    else:
        return recursive_function(n-1, sum+n)

c = 998
print(recursive_function(c, 0))

它一直运行到n=997,然后就中断并输出RecursionError: maximum recursion depth exceeded in comparison。这只是堆栈溢出吗?有办法解决它吗?


解决方案 1:

是的,这是防止堆栈溢出的措施。Python(或者更确切地说,CPython 实现)不会优化尾部递归,而不受限制的递归会导致堆栈溢出。您可以使用以下命令检查递归限制sys.getrecursionlimit

import sys
print(sys.getrecursionlimit())

并使用以下方法更改递归限制sys.setrecursionlimit

sys.setrecursionlimit(1500)

但这样做很危险——标准限制有点保守,但 Python 堆栈框架可能非常大。

Python 不是函数式语言,尾部递归不是一种特别有效的技术。如果可能的话,迭代重写算法通常是一个更好的主意。

解决方案 2:

看起来您只需要设置更高的递归深度:

import sys
sys.setrecursionlimit(1500)

解决方案 3:

如果您经常需要更改递归限制(例如在解决编程难题时),您可以定义一个简单的上下文管理器,如下所示:

import sys

class recursionlimit:
    def __init__(self, limit):
        self.limit = limit

    def __enter__(self):
        self.old_limit = sys.getrecursionlimit()
        sys.setrecursionlimit(self.limit)

    def __exit__(self, type, value, tb):
        sys.setrecursionlimit(self.old_limit)

然后要调用具有自定义限制的函数,您可以执行以下操作:

with recursionlimit(1500):
    print(fib(1000, 0))

退出语句主体时,with递归限制将恢复为默认值。

PS 您可能还想增加 Python 进程的堆栈大小,以适应较大的递归限制值。例如,可以通过ulimitshell 内置命令或文件来实现。limits.conf(5)

解决方案 4:

这是为了避免堆栈溢出。Python 解释器限制递归的深度,以帮助您避免无限递归,从而导致堆栈溢出。尝试增加递归限制(sys.setrecursionlimit)或重写没有递归的代码。

来自Python 文档:

sys.getrecursionlimit()

返回递归限制的当前值,即 Python 解释器堆栈的最大深度。此限制可防止无限递归导致 C 堆栈溢出并使 Python 崩溃。它可以通过 来设置setrecursionlimit()

解决方案 5:

resource.setrlimit还必须用来增加堆栈大小并防止段错误

Linux内核对进程的堆栈进行了限制。

Python 将局部变量存储在解释器的堆栈中,因此递归会占用解释器的堆栈空间。

如果 Python 解释器试图超出堆栈限制,Linux 内核就会产生段错误。

堆栈限制大小由getrlimitsetrlimit系统调用控制。

Python 通过模块提供对这些系统调用的访问resource

sys.setrecursionlimit例如在https://stackoverflow.com/a/3323013/895245中提到的,仅增加了 Python 解释器自身对其堆栈大小施加的限制,但并没有触及 Linux 内核对 Python 进程施加的限制。

示例程序:

主程序

import resource
import sys

print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print

# Will segfault without this line.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)

def f(i):
    print i
    sys.stdout.flush()
    f(i + 1)
f(0)

当然,如果你继续增加setrlimit,你的 RAM 最终会耗尽,这要么会让你的计算机因交换疯狂而停止运行,要么通过OOM Killer杀死 Python 。

在 bash 中,你可以使用以下命令查看并设置堆栈限制(以 kb 为单位):

ulimit -s
ulimit -s 10000

我的默认值是 8Mb。

参见:

  • 在 Python 脚本中设置堆栈大小

  • Linux、Mac 和 Windows 的硬递归限制是多少?

在 Ubuntu 16.10、Python 2.7.12 上测试。

解决方案 6:

使用能够保证尾部调用优化的语言。或者使用迭代。或者,使用装饰器。

解决方案 7:

我意识到这是一个老问题,但对于那些阅读的人来说,我建议不要对此类问题使用递归 - 列表要快得多,并且完全避免了递归。 我将按如下方式实现它:

def fibonacci(n):
    f = [0,1,1]
    for i in xrange(3,n):
        f.append(f[i-1] + f[i-2])
    return 'The %.0fth fibonacci number is: %.0f' % (n,f[-1])

(如果从 0 而不是 1 开始计算斐波那契数列,请在 xrange 中使用 n+1。)

解决方案 8:

我遇到了类似的问题,错误为“超出最大递归深度”。我发现该错误是由我循环的目录中的损坏文件触发的os.walk。如果您在解决此问题时遇到困难,并且您正在处理文件路径,请务必缩小范围,因为它可能是损坏的文件。

解决方案 9:

当然,斐波那契数列可以通过应用比奈公式在 O(n) 内计算:

from math import floor, sqrt

def fib(n):                                                     
    return int(floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))+0.5))

正如评论者指出的那样,由于 ,它不是 O(1) 而是 O(n) 2**n。另一个区别是,您只能获得一个值,而使用递归,您可以获得Fibonacci(n)直到该值的所有值。

解决方案 10:

如果只想得到几个斐波那契数,可以使用矩阵方法。

from numpy import matrix

def fib(n):
    return (matrix('0 1; 1 1', dtype='object') ** n).item(1)

它很快,因为 numpy 使用快速指数算法。您可以在 O(log n) 内得到答案。它比 Binet 公式更好,因为它只使用整数。但是如果你想要所有不超过 n 的斐波那契数,那么最好通过记忆来完成。

解决方案 11:

我们可以使用@lru_cache装饰器和setrecursionlimit()方法来实现这一点:

import sys
from functools import lru_cache

sys.setrecursionlimit(15000)


@lru_cache(128)
def fib(n: int) -> int:
    if n == 0:
        return 0
    if n == 1:
        return 1

    return fib(n - 2) + fib(n - 1)


print(fib(14000))

输出

3002468761178461090995494179715025648692747937490792943468375429502230242942284835863402333575216217865811638730389352239181342307756720414619391217798542575996541081060501905302157019002614964717310808809478675602711440361241500732699145834377856326394037071666274321657305320804055307021019793251762830816701587386994888032362232198219843549865275880699612359275125243457132496772854886508703396643365042454333009802006384286859581649296390803003232654898464561589234445139863242606285711591746222880807391057211912655818499798720987302540712067959840802106849776547522247429904618357394771725653253559346195282601285019169360207355179223814857106405285007997547692546378757062999581657867188420995770650565521377874333085963123444258953052751461206977615079511435862879678439081175536265576977106865074099512897235100538241196445815568291377846656352979228098911566675956525644182645608178603837172227838896725425605719942300037650526231486881066037397866942013838296769284745527778439272995067231492069369130289154753132313883294398593507873555667211005422003204156154859031529462152953119957597195735953686798871131148255050140450845034240095305094449911578598539658855704158240221809528010179414493499583473568873253067921639513996596738275817909624857593693291980841303291145613566466575233283651420134915764961372875933822262953420444548349180436583183291944875599477240814774580187144637965487250578134990402443365677985388481961492444981994523034245619781853365476552719460960795929666883665704293897310201276011658074359194189359660792496027472226428571547971602259808697441435358578480589837766911684200275636889192254762678512597000452676191374475932796663842865744658264924913771676415404179920096074751516422872997665425047457428327276230059296132722787915300105002019006293320082955378715908263653377755031155794063450515731009402407584683132870206376994025920790298591144213659942668622062191441346200098342943955169522532574271644954360217472458521489671859465232568419404182043966092211744372699797375966048010775453444600153524772238401414789562651410289808994960533132759532092895779406940925252906166612153699850759933762897947175972147868784008320247586210378556711332739463277940255289047962323306946068381887446046387745247925675240182981190836264964640612069909458682443392729946084099312047752966806439331403663934969942958022237945205992581178803606156982034385347182766573351768749665172549908638337611953199808161937885366709285043276595726484068138091188914698151703122773726725261370542355162118164302728812259192476428938730724109825922331973256105091200551566581350508061922762910078528219869913214146575557249199263634241165352226570749618907050553115468306669184485910269806225894530809823102279231750061652042560772530576713148647858705369649642907780603247428680176236527220826640665659902650188140474762163503557640566711903907798932853656216227739411210513756695569391593763704981001125

来源

函数工具 lru_cache

解决方案 12:

RecursionError:比较时超出最大递归深度

解决方案 :

首先,最好知道当你在 Python 中对大型输入(> 10^4)执行递归函数时,可能会遇到“超出最大递归深度错误”。

Python 中的 sys 模块有一个函数 getrecursionlimit() 可以显示你的 Python 版本中的递归限制。

import sys
print("Python Recursive Limitation = ", sys.getrecursionlimit())

某些版本的 Python 的默认值为 1000,而其他版本的默认值为 1500

您可以更改此限制,但需要注意的是,如果您将其增加太多,将会出现内存溢出错误。

因此在增加它之前要小心。您可以使用 setrecursionlimit() 在 Python 中增加此限制。

import sys
sys.setrecursionlimit(3000)

请点击此链接获取有关导致此问题的原因的更多信息:

https://elvand.com/quick-sort-binary-search/

解决方案 13:

正如@alex所建议的,您可以使用生成器函数按顺序而不是递归地执行此操作。

以下是您问题中的代码的等效内容:

def fib(n):
    def fibseq(n):
        """ Iteratively return the first n Fibonacci numbers, starting from 0. """
        a, b = 0, 1
        for _ in xrange(n):
            yield a
            a, b = b, a + b

    return sum(v for v in fibseq(n))

print format(fib(100000), ',d')  # -> no recursion depth error

解决方案 14:

编辑:6 年后我意识到我的“使用生成器”是轻率的,并没有回答问题。我很抱歉。

我想我的第一个问题是:你真的需要更改递归限制吗?如果不需要,那么也许我的或任何其他不涉及更改递归限制的答案将适用。否则,如上所述,使用覆盖递归限制sys.getrecursionlimit(n)

使用发电机吗?

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fibs = fib() #seems to be the only way to get the following line to work is to
             #assign the infinite generator to a variable

f = [fibs.next() for x in xrange(1001)]

for num in f:
        print num

以上fib()函数改编自《Python 生成器简介》。

解决方案 15:

许多人建议增加递归限制是一个很好的解决方案,但事实并非如此,因为总是会有限制。相反,请使用迭代解决方案。

def fib(n):
    a,b = 1,1
    for i in range(n-1):
        a,b = b,a+b
    return a
print fib(5)

解决方案 16:

我想给你一个使用记忆法计算斐波那契的例子,因为这将允许你使用递归计算更大的数字:

cache = {}
def fib_dp(n):
    if n in cache:
        return cache[n]
    if n == 0: return 0
    elif n == 1: return 1
    else:
        value = fib_dp(n-1) + fib_dp(n-2)
    cache[n] = value
    return value

print(fib_dp(998))

这仍然是递归的,但使用了一个简单的哈希表,允许重用以前计算的斐波那契数,而不是再次执行它们。

解决方案 17:

import sys
sys.setrecursionlimit(1500)

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))

解决方案 18:

我不确定我是否在重复别人的话,但不久前,一些好心人为递归调用函数编写了 Y 运算符,例如:

def tail_recursive(func):
  y_operator = (lambda f: (lambda y: y(y))(lambda x: f(lambda *args: lambda: x(x)(*args))))(func)
  def wrap_func_tail(*args):
    out = y_operator(*args)
    while callable(out): out = out()
    return out
  return wrap_func_tail

然后递归函数需要形成:

def my_recursive_func(g):
  def wrapped(some_arg, acc):
    if <condition>: return acc
    return g(some_arg, acc)
  return wrapped

# and finally you call it in code

(tail_recursive(my_recursive_func))(some_arg, acc)

对于斐波那契数,你的函数如下所示:

def fib(g):
  def wrapped(n_1, n_2, n):
    if n == 0: return n_1
    return g(n_2, n_1 + n_2, n-1)
  return wrapped

print((tail_recursive(fib))(0, 1, 1000000))

输出:

..684684301719893411568996526838242546875

(实际上是数字的音调)

解决方案 19:

我们还可以使用动态规划自下而上的方法

def fib_bottom_up(n):

    bottom_up = [None] * (n+1)
    bottom_up[0] = 1
    bottom_up[1] = 1

    for i in range(2, n+1):
        bottom_up[i] = bottom_up[i-1] + bottom_up[i-2]

    return bottom_up[n]

print(fib_bottom_up(20000))
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用