web-dev-qa-db-fra.com

Erreur de fonction récursive Python: "profondeur maximale de récursivité dépassée"

J'ai résolu le problème 10 de Project Euler avec le code suivant, qui fonctionne par force brute:

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)))

Les trois fonctions fonctionnent comme suit: 

  1. isPrime vérifie si un nombre est un nombre premier;
  2. primeList renvoie une liste contenant un ensemble de nombres premiers pour une certaine plage avec la limite 'n', et; 
  3. sumPrimes résume les valeurs de tous les nombres d'une liste. (Cette dernière fonction n'est pas nécessaire, mais j'ai aimé la clarté, en particulier pour un débutant comme moi.)

J'ai ensuite écrit une nouvelle fonction, primeListRec , qui fait exactement la même chose que primeList , pour m'aider à mieux comprendre la récursivité:

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

La fonction récursive ci-dessus a fonctionné, mais seulement pour de très petites valeurs, comme '500'. La fonction a provoqué le blocage de mon programme lorsque j'ai mis «1000». Et quand j'ai mis une valeur comme '2000', Python m'a donné ceci: 

RuntimeError: profondeur maximale de récursivité dépassée .

Qu'est-ce que j'ai mal fait avec ma fonction récursive? Ou existe-t-il un moyen spécifique d'éviter une limite de récursivité? 

18
anonnoir

La récursivité n’est pas la façon la plus idiomatique de faire des choses en Python, car elle n’a pas tail récursivité optimisation, rendant ainsi peu pratique l’utilisation de la récursivité en remplacement de récursif, cela n’aiderait pas de toute façon). En gros, cela signifie que vous ne devriez pas l'utiliser pour des choses dont la complexité est supérieure à linéaire si vous vous attendez à ce que vos entrées soient volumineuses. ).

Si vous voulez essayer cette approche, utilisez un langage plus adapté à la programmation fonctionnelle, tel que LISP, Scheme, Haskell, OCaml, etc. ou essayez Stackless Python, qui a des limites plus larges dans l’utilisation de la pile et qui a également une optimisation de la récursion de la queue :-)

À propos, un équivalent de votre fonction récursif pourrait être:

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 []))

Un autre "à propos", vous ne devriez pas construire de liste si vous l'utilisez simplement pour additionner les valeurs ... La façon pythonique de résoudre le 10ème problème de Project Euler est la suivante:

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

(OK, peut-être que le fendre en plusieurs lignes serait encore plus pythonique, mais j'adore les doublures ^ _ ^)

26
fortran

Comme déjà dit, dans les langues qui ne peuvent pas traiter les piles profondes, il vaut mieux adopter une approche itérative. Dans votre cas, en particulier, il est préférable de changer l'algorithme utilisé. Je suggère d'utiliser le Sieve of Eratosthenes pour trouver la liste des nombres premiers. Ce sera plus rapide que votre programme actuel.

1
Thiago Chaves

Eh bien, je ne suis pas un expert en python, mais je suppose que vous avez atteint la limite pile . C’est le problème de la récursion. C’est génial quand on n’a pas à récidiver très souvent, mais nul quand le nombre de récursions devient modérément élevé.

L'alternative idéale consiste à réécrire votre algorithme pour utiliser l'itération à la place.

Edit: En fait, après avoir examiné votre erreur spécifique de près, vous pouvez la surmonter en modifiant sys.getrecursionlimit . Cela ne vous mènera que jusqu'à présent si. Finalement, vous obtiendrez une exception stackoverflow qui me ramènera à mon point d'origine.

1
Adrian

Vous parcourez n nombres et récursez à chaque étape. Par conséquent, la limite de récursivité de Python définit votre nombre maximal d'entrées. C'est évidemment indésirable. Surtout parce que les problèmes d'Euler concernent généralement de très grands nombres.

0
Johannes Charra