You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python设置sys.setrecursionlimit后仍触发递归深度超限错误的原因排查

Python设置sys.setrecursionlimit后仍触发递归深度超限错误的原因排查

咱们先来梳理下你的问题:你明明把sys.setrecursionlimit设到了20000,但调用f(4952)这类大参数时还是触发了递归深度超限错误,错误提示里显示重复调用了996次后终止,最终结果预期是9200,你想不通为什么设置了更大的限制还是没用。

先看你提供的代码:

import sys
from functools import *

sys.setrecursionlimit(20000)

@lru_cache(None)
def f(n):
    if n <= 3:
        return n - 1
    if n > 3:
        if n % 2 == 0:
            return f(n - 2) + (n / 2) - f(n - 4)
        else:
            return f(n - 1) * n + f(n - 2)

result = f(4952) + 2 * f(4958) + f(4964)
print(result)

触发的错误信息如下:

Traceback (most recent call last):
  File "-", line 21, in <module>
    result = f(4952) + 2 * f(4958) + f(4964)
             ^^^^^^^
  File "-", line 14, in f
    return f(n - 2) + (n / 2) - f(n - 4)
           ^^^^^^^^
  File "-", line 14, in f
    return f(n - 2) + (n / 2) - f(n - 4)
           ^^^^^^^^
  File "-", line 14, in f
    return f(n - 2) + (n / 2) - f(n - 4)
           ^^^^^^^^
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded

原因分析

从错误里能看到,递归链只重复了996次就触发了错误,这个次数非常接近Python默认的1000层递归限制,说明你设置的20000很可能没真正生效,或者还有其他限制在起作用:

  1. 运行环境限制了递归深度的修改
    有些在线IDE、沙箱环境会刻意限制sys.setrecursionlimit的修改,强制保留默认的递归深度来避免资源耗尽。你可以在设置后加一行代码验证:

    print(sys.getrecursionlimit())  # 看看实际输出是不是20000
    

    如果输出是1000,那说明你的设置被环境拦截了。

  2. 操作系统线程栈空间的隐性限制
    就算sys.setrecursionlimit生效了,Python的递归调用还受操作系统分配给线程的栈空间大小限制。每个递归调用都会在栈里保存局部变量、返回地址等信息,当栈空间被耗尽时,哪怕解释器允许更大的深度,也会触发递归错误。对于n=4952,递归链长度是(4952-2)/2 = 2475层,理论上20000的限制足够,但如果环境给的栈空间太小,也会提前报错。

  3. 递归逻辑的深度积累
    虽然你用了lru_cache缓存结果,但第一次计算f(4952)时,必须从4952递归到2,这个完整的递归链是一次性执行的,中间的f(n-4)虽然会被缓存,但它的调用是在f(n-2)的递归栈完成之后,所以整个递归深度还是由最长的那条链决定。

解决方法

最稳妥的方式是把递归改成迭代,彻底绕开递归深度的问题,效率也更高:

def compute_f(max_n):
    f_cache = {}
    # 先处理基础情况
    for n in range(1, 4):
        f_cache[n] = n - 1
    # 从4开始向上计算到max_n
    for n in range(4, max_n + 1):
        if n % 2 == 0:
            f_cache[n] = f_cache[n-2] + (n / 2) - f_cache[n-4]
        else:
            f_cache[n] = f_cache[n-1] * n + f_cache[n-2]
    return f_cache

# 计算到需要的最大值4964
f_cache = compute_f(4964)
result = f_cache[4952] + 2 * f_cache[4958] + f_cache[4964]
print(result)

这个代码从最小的n开始向上计算,用字典保存中间结果,完全没有递归调用,不管n多大都不会有深度问题,而且能直接得到预期的9200。

如果一定要用递归,可以先验证sys.setrecursionlimit是否生效,尝试设置更大的值(比如100000),但这种方法依赖环境,风险也更高。

备注:内容来源于stack exchange,提问作者lumba

火山引擎 最新活动