为什么我只有在同一行上调用递归函数两次才会得到RecursionError?

时间:2017-03-16 12:14:57

标签: python recursion

我正在使用Python中的递归函数。我使用memoization来加速计算。

如果RecursionError> = 333(YMMV),则以下代码段失败并显示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

    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()函数会影响它是否超过递归深度?

1 个答案:

答案 0 :(得分:2)

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

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.这绝对容易受到递归限制的影响。