Python递归函数错误:"超出最大递归深度"

ano*_*oir 22 python recursion

我使用以下代码解决了Euler项目的问题10,该代码通过强力实施:

def isPrime(n):

    for x in range(2, int(n**0.5)+1):
        if n % x == 0:
            return False
    return True


def primeList(n):

    primes = []

    for i in range(2,n):
        if isPrime(i):
            primes.append(i)

    return primes


def sumPrimes(primelist):
    prime_sum = sum(primelist)
    return prime_sum


print (sumPrimes(primeList(2000000)))
Run Code Online (Sandbox Code Playgroud)

这三个功能的工作原理如下:

  1. isPrime检查一个数字是否是素数;
  2. primeList返回一个列表,其中包含一组限制为"n"的特定范围的素数,并且;
  3. sumPrimes总结列表中所有数字的值.(不需要最后一个功能,但我喜欢它的清晰度,特别是像我这样的初学者.)

然后我编写了一个新函数primeListRec,它与primeList完全相同,以帮助我更好地理解递归:

def primeListRec(i, n):
    primes = []
    #print i


    if (i != n):
        primes.extend(primeListRec(i+1,n))

    if (isPrime(i)):
        primes.append(i)
        return primes


    return primes
Run Code Online (Sandbox Code Playgroud)

上面的递归函数有效,但仅适用于非常小的值,如'500'.当我输入'1000'时,该功能导致我的程序崩溃.当我输入类似'2000'的值时,Python给了我这个:

RuntimeError:超出最大递归深度.

我的递归函数出了什么问题?或者是否有一些特定的方法来避免递归限制?

for*_*ran 31

递归不是在Python中做事的最常用的方法,因为它没有尾递归优化,因此使用递归作为迭代的替代是不切实际的(即使在你的例子中函数不是尾递归的,也就是说无论如何都没有帮助.基本上,这意味着如果你希望你的输入很大,你就不应该把它用于复杂度大于线性的东西(对于那些具有对数递归深度的事情来说,这仍然是可以的,比如像QuickSort那样划分和征服算法).

如果你想尝试这种方法,可以使用一种更适合进行函数式编程的语言,如Lisp,Scheme,Haskell,OCaml等; 或尝试使用Stackless Python,它在堆栈使用方面有更广泛的限制,并且还具有尾递归优化:-)

顺便说一句,你的函数的尾递归等价物可能是:

def primeList(n, i=2, acc=None):
    return i > n and (acc or []) or primeList(n, i+1, (acc or []) + (isPrime(i) and [i] or []))
Run Code Online (Sandbox Code Playgroud)

另一个"顺便说一句",你不应该构建一个列表,如果你只是为了将值加起来......解决Project Euler的第10个问题的Pythonic方法是:

print sum(n for n in xrange(2, 2000001) if all(n % i for i in xrange(2, int(n**0.5)+1)))
Run Code Online (Sandbox Code Playgroud)

(好吧,也许在不同的行中拆分它会更加Pythonic,但我喜欢一个衬里^ _ ^)

  • 当迭代版本无论如何都需要堆栈时(例如遍历树,产生排列等)并且递归深度限制在合理的大小,这是一种可行的方法.在这种情况下,你试图以一种人为的方式使用递归,因为它显然是你需要的for循环. (4认同)
  • 在这种情况下,尾递归无济于事,因为递归调用不是函数中的最后一个操作. (3认同)
  • @ user283169我想说的正是我所说的:递归不应该被**滥用**.如果你需要它,因为你的问题本质上是递归的,请使用它,但不要试图用递归替换迭代,因为这不是它的意思. (2认同)