【问题标题】:Why I'm getting a RecursionError only if I call the recursive function twice on the same line?为什么只有在同一行上调用递归函数两次时才会收到 RecursionError?
【发布时间】:2017-03-16 12:14:57
【问题描述】:

我正在使用 Python 中的递归函数。我使用记忆来加快计算速度。

如果n >= 333 (YMMV),则以下 sn-p 失败并返回 RecursionError(“比较中超出最大递归深度”):

from scipy.special import binom
from sys import argv

def memoized(func):
    cache = {}
    def new_func(*args):
        if args in cache:
             return cache[args]
        else:
             temp = cache[args] = func(*args)
             return temp
    return new_func

def p(N, n, k):
    return binom(n, k) * (1 / N)**k * (1 - 1/N)**(n-k)

@memoized
def X(n):
    if n <= 1:
        return 1

    s = 0
    for k in range(1,n):
        s += p(2, n, k) * (X(k)+X(n-k))

    return (1 + (1/2)**(n-1) * X(0) + s) / (1 - (1/2)**(n-1))

n = int(argv[1])
print('efficiency: ' + str(X(n) / n))

但是,如果我将 for 循环分成两个不同的总和,它会按预期工作,n 的值更大:

from scipy.special import binom
from sys import argv

def memoized(func):
    cache = {}
    def new_func(*args):
        if args in cache:
             return cache[args]
        else:
             temp = cache[args] = func(*args)
             return temp
    return new_func

def p(N, n, k):
    return binom(n, k) * (1 / N)**k * (1 - 1/N)**(n-k)

@memoized
def X(n):
    if n <= 1:
        return 1

    s1 = 0
    for k in range(1,n):
        s1 += p(2, n, k) * X(k)

    s2 = 0
    for k in range(1,n):
        s2 += p(2, n, k) * X(n-k)

    return (1 + (1/2)**(n-1) * X(0) + s1+s2) / (1 - (1/2)**(n-1))

n = int(argv[1])
print('efficiency: ' + str(X(n) / n))

这是为什么呢?为什么同一行调用两次X()函数会影响是否超过递归深度?

【问题讨论】:

  • 尝试记录正在调用的 n 的值,或者用铅笔和纸手工追踪它。
  • @JoshLee 第一个 sn-p 使用 n-1 直到达到 1,第二个 sn-p 反过来(1 到 n

标签: python recursion


【解决方案1】:

从您的第二个(工作)示例开始:

for k in range(1,n):
    s1 += p(2, n, k) * X(k)

您要做的第一件事是执行X(),其值从1 开始计数到k。每次遍历此循环时,它最多会递归到 X() 一次,因为所有先前的值都已被记忆。所以递归限制没有问题。然后你就到这里了:

for k in range(1,n):
    s2 += p(2, n, k) * X(n-k)

这只是倒计时n 的值与您之前使用的相同(以相反的顺序),因此所有对X() 的调用都已被记忆。没问题。事实上,有了 memoization,这个版本就几乎没有递归了。

现在看看你的第一个例子:

for k in range(1,n):
    s += p(2, n, k) * (X(k)+X(n-k))

第一次循环时,k 是 1。你会调用 X(1),这很好,但你会直接转到 X(n-1)。由于除了 X(1) 之外您还没有记住任何其他内容,这将启动一个递归链,该链将一直计数到 1。这肯定会受到递归限制的影响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多