web-dev-qa-db-fra.com

Quelle est la profondeur de récursivité maximale en Python et comment l'augmenter?

J'ai cette fonction récursive de queue ici:

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))

Cela fonctionne jusqu’à n = 997, puis il se brise et crache une "profondeur de récursivité maximale dépassée par rapport à la comparaison" RuntimeError. Est-ce juste un débordement de pile? Y a-t-il un moyen de le contourner?

314
quantumSoup

C'est un garde contre un débordement de pile, oui. Python (ou plutôt, l'implémentation CPython) n'optimise pas la récursion finale, et la récursivité débridée provoque des débordements de pile. Vous pouvez vérifier la limite de récursivité avec sys.getrecursionlimit et modifier la limite de récursivité avec sys.setrecursionlimit , mais cela est dangereux - la limite standard est un peu conservateur, mais Python stackframes peut être assez gros.

Python n'est pas un langage fonctionnel et la récursion de la queue n'est pas une technique particulièrement efficace. Réécrire l'algorithme de manière itérative, si possible, est généralement une meilleure idée.

359
Thomas Wouters

On dirait qu'il suffit de définir une profondeur de récursion supérieure

sys.setrecursionlimit(1500)
102
David Young

C'est pour éviter un débordement de pile. L'interprète Python limite les profondeurs de récursivité pour vous aider à éviter des récursions infinies, entraînant des débordements de pile. Essayez d'augmenter la limite de récursivité (sys.setrecursionlimit) ou de réécrire votre code sans récursivité.

de site web python :

sys.getrecursionlimit()

Renvoie la valeur actuelle de la limite de récursivité, la profondeur maximale de la pile d'interpréteur Python. Cette limite empêche la récursion infinie de provoquer un débordement de la pile C et un crash de Python. Il peut être défini avec setrecursionlimit ().

46
Scharron

Utilisez un langage qui garantit une optimisation de l'appel final. Ou utilisez itération. Sinon, soyez mignon avec décorateurs .

17
Marcelo Cantos

Si vous devez souvent modifier la limite de récursivité (par exemple, tout en résolvant des énigmes de programmation), vous pouvez définir un simple gestionnaire de contexte comme ceci:

import sys

class recursionlimit:
    def __init__(self, limit):
        self.limit = limit
        self.old_limit = sys.getrecursionlimit()

    def __enter__(self):
        sys.setrecursionlimit(self.limit)

    def __exit__(self, type, value, tb):
        sys.setrecursionlimit(self.old_limit)

Ensuite, pour appeler une fonction avec une limite personnalisée, vous pouvez faire:

with recursionlimit(1500):
    print(fib(1000, 0))

À la sortie du corps de l'instruction with, la limite de récursivité sera restaurée à la valeur par défaut.

14
Eugene Yarmash

Je me rends compte que c’est une vieille question, mais pour ceux qui lisent, je déconseille d’utiliser la récursivité pour des problèmes tels que celui-ci - les listes sont beaucoup plus rapides et évitent totalement la récursivité. Je mettrais ceci en application en tant que:

def fibonacci(n):
    f = [0,1,1]
    for i in xrange(3,n):
        f.append(f[i-1] + f[i-2])
    return 'The %.0fth fibonacci number is: %.0f' % (n,f[-1])

(Utilisez n + 1 dans xrange si vous commencez à compter votre séquence de fibonacci à partir de 0 au lieu de 1.)

10
Daniel

Bien entendu, les nombres de Fibonacci peuvent être calculés dans O(n) en appliquant la formule de Binet:

from math import floor, sqrt

def fib(n):                                                     
    return int(floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))+0.5))

Comme le notent les commentateurs, ce n'est pas O(1) mais O(n) à cause de 2**n. Une différence est également que vous n'obtenez qu'une valeur, alors qu'avec la récursivité, vous obtenez toutes les valeurs de Fibonacci(n) jusqu'à cette valeur.

9
rwst

resource.setrlimit doit également être utilisé pour augmenter la taille de la pile et éviter les erreurs de segmentation

Le noyau Linux limite la pile de processus.

Python stocke les variables locales sur la pile de l'interpréteur et la récursion occupe donc l'espace de la pile de l'interpréteur.

Si l'interprète Python tente de dépasser la limite de pile, le noyau Linux attribue une erreur de segmentation.

La taille de la limite de pile est contrôlée avec les appels système getrlimit et setrlimit.

Python offre un accès à ces appels système via le module resource.

import resource
import sys

print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print

# Will segfault without this line.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)

def f(i):
    print i
    sys.stdout.flush()
    f(i + 1)
f(0)

Bien sûr, si vous continuez à augmenter ulimit, votre RAM s'épuisera, ce qui ralentira votre ordinateur à cause d'un swap madness ou tuera Python via le tueur de MOO.

À partir de bash, vous pouvez voir et définir la limite de pile (en Ko) avec:

ulimit -s
ulimit -s 10000

La valeur par défaut pour moi est 8Mb.

Voir également:

Testé sur Ubuntu 16.10, Python 2.7.12.

J'ai eu un problème similaire avec l'erreur "Profondeur de récursivité maximale dépassée". J'ai découvert que l'erreur était déclenchée par un fichier corrompu dans le répertoire sur lequel j'étais en boucle avec os.walk. Si vous rencontrez des difficultés pour résoudre ce problème et que vous travaillez avec des chemins de fichiers, veillez à le limiter, car il pourrait s'agir d'un fichier corrompu.

6
Tyler

Utiliser des générateurs?

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fibs = fib() #seems to be the only way to get the following line to work is to
             #assign the infinite generator to a variable

f = [fibs.next() for x in xrange(1001)]

for num in f:
        print num

au-dessus de la fonction fib () adaptée de: http://intermediatepythonista.com/python-generators

4
alex

Si vous souhaitez obtenir seulement quelques nombres de Fibonacci, vous pouvez utiliser la méthode de la matrice.

from numpy import matrix

def fib(n):
    return (matrix('0 1; 1 1', dtype='object') ** n).item(1)

C'est rapide, car numpy utilise un algorithme d'exponentiation rapide. Vous obtenez une réponse en O (log n). Et c'est mieux que la formule de Binet car elle utilise uniquement des entiers. Mais si vous voulez tous les nombres de Fibonacci jusqu'à n, il est préférable de le faire par mémorisation.

4
bebidek

Beaucoup recommandent d'augmenter la limite de récursivité, mais ce n'est pas parce qu'il y aura toujours une limite. Utilisez plutôt une solution itérative.

def fib(n):
    a,b = 1,1
    for i in range(n-1):
        a,b = b,a+b
    return a
print fib(5)
2
Harun ERGUL

Je voulais vous donner un exemple d'utilisation de la mémoisation pour calculer Fibonacci, car cela vous permettra de calculer des nombres beaucoup plus grands à l'aide de la récursivité:

cache = {}
def fib_dp(n):
    if n in cache:
        return cache[n]
    if n == 0: return 0
    Elif n == 1: return 1
    else:
        value = fib_dp(n-1) + fib_dp(n-2)
    cache[n] = value
    return value

print(fib_dp(998))

Ceci est toujours récursif, mais utilise une table de hachage simple qui permet la réutilisation des nombres de Fibonacci calculés précédemment au lieu de les refaire.

1
user3393266

Comme @alex suggéré , vous pouvez utiliser une fonction de générateur pour le faire. Voici l'équivalent du code dans votre question:

def fib(n):
    def fibseq(n):
        """ Iteratively return the first n Fibonacci numbers, starting from 0 """
        a, b = 0, 1
        for _ in xrange(n):
            yield a
            a, b = b, a + b

    return sum(v for v in fibseq(n))

print format(fib(100000), ',d')  # -> no recursion depth error
1
martineau
import sys
sys.setrecursionlimit(1500)

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))
1
user11462758