Was ist die maximale Rekursionstiefe in Python und wie kann sie erhöht werden?

Lesezeit: 8 Minuten

Benutzer-Avatar
QuantenSuppe

Ich habe diese tail rekursive Funktion hier:

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

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

Es funktioniert bis zu n=997dann bricht es einfach ab und spuckt einen aus RecursionError: maximum recursion depth exceeded in comparison. Ist das nur ein Stapelüberlauf? Gibt es eine Möglichkeit, es zu umgehen?

  • Siehe auch stackoverflow.com/questions/5061582/…

    – Thomas Ahle

    28. April 2014 um 19:09 Uhr

  • Auswendiglernen könnte Ihre Funktion beschleunigen und ihre effektive rekursive Tiefe erhöhen, indem zuvor berechnete Werte beendet werden, anstatt die Stapelgröße zu erhöhen.

    – Cyoce

    11. Januar 2016 um 18:47 Uhr

  • Die Rekursionsgrenze beträgt normalerweise 1000.

    – Boris Werchowskij

    24. April 2019 um 7:29 Uhr

  • @tonix der Interpreter fügt einen Stapelrahmen hinzu (die line <n>, in <module> in Stack-Traces) und dieser Code benötigt 2 Stack-Frames für n=1 (weil der Basisfall ist n < 1so für n=1 es kommt immer noch vor). Und ich denke, das Rekursionslimit ist nicht inklusive, da es sich um “Fehler beim Erreichen von 1000” und nicht um “Fehler beim Überschreiten von 1000 (1001)” handelt. 997 + 2 ist weniger als 1000, also funktioniert es 998 + 2 nicht, weil es an die Grenze stößt.

    – Boris Werchowskij

    15. Dezember 2019 um 19:00 Uhr

  • @tonix nein. recursive_function(997) funktioniert, es bricht an 998. Wenn du anrufst recursive_function(998) Es verwendet 999 Stack-Frames und 1 Frame wird vom Interpreter hinzugefügt (da Ihr Code immer so ausgeführt wird, als wäre er Teil des Moduls der obersten Ebene), wodurch er die 1000-Grenze erreicht.

    – Boris Werchowskij

    15. Dezember 2019 um 19:49 Uhr

Benutzer-Avatar
Thomas Wouters

Es ist ein Schutz gegen einen Stapelüberlauf, ja. Python (oder besser gesagt die CPython-Implementierung) optimiert die Schwanzrekursion nicht, und ungezügelte Rekursion verursacht Stapelüberläufe. Sie können das Rekursionslimit mit überprüfen sys.getrecursionlimit:

import sys
print(sys.getrecursionlimit())

und ändern Sie die Rekursionsgrenze mit sys.setrecursionlimit:

sys.setrecursionlimit(1500)

Dies ist jedoch gefährlich – die Standardgrenze ist ein wenig konservativ, aber Python-Stackframes können ziemlich groß sein.

Python ist keine funktionale Sprache und Schwanzrekursion ist keine besonders effiziente Technik. Wenn möglich, ist es im Allgemeinen eine bessere Idee, den Algorithmus iterativ neu zu schreiben.

Benutzer-Avatar
David Jung

Sieht so aus, als müsstest du es einfach tun Stellen Sie eine höhere Rekursionstiefe ein:

import sys
sys.setrecursionlimit(1500)

  • In meinem Fall habe ich die return-Anweisung im Basisfall vergessen und es ging weiter, um 1000 zu überschreiten. Python fing an, diese Ausnahme auszulösen, und ich war erstaunt, weil ich mir über das Nein sicher war. von Stapeln, die es erstellen wird, um es auszuführen.

    – vijayraj34

    19. Mai 2019 um 8:07 Uhr

  • sys.setrecursionlimit(50) oder ein kleiner Betrag ist nützlich, wenn Ihr Programm in Rekursion eintritt und Sie möchten, dass die Fehlermeldung NICHT seitenweise denselben Text enthält. Ich fand das sehr hilfreich beim Debuggen von (meinem) schlechten rekursiven Code.

    – Peawormsworth

    22. Februar 2020 um 0:38 Uhr

Benutzer-Avatar
Scharron

Es soll einen Stapelüberlauf vermeiden. Der Python-Interpreter begrenzt die Rekursionstiefe, um Ihnen zu helfen, unendliche Rekursionen zu vermeiden, die zu Stapelüberläufen führen. Versuchen Sie, das Rekursionslimit zu erhöhen (sys.setrecursionlimit) oder Ihren Code ohne Rekursion neu schreiben.

Von dem Python-Dokumentation:

sys.getrecursionlimit()

Gibt den aktuellen Wert des Rekursionslimits zurück, die maximale Tiefe des Python-Interpreter-Stacks. Diese Grenze verhindert, dass die unendliche Rekursion einen Überlauf des C-Stacks verursacht und Python zum Absturz bringt. Es kann durch eingestellt werden setrecursionlimit().

  • Auf meinem Anaconda x64, 3.5 Python unter Windows ist das Standardlimit 1000.

    – Guillaume Chevalier

    4. Dezember 2015 um 21:48 Uhr

Benutzer-Avatar
Eugen Yarmash

Wenn Sie die Rekursionsgrenze oft ändern müssen (z. B. beim Lösen von Programmierrätseln), können Sie eine einfache definieren Kontextmanager so was:

import sys

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

    def __enter__(self):
        self.old_limit = sys.getrecursionlimit()
        sys.setrecursionlimit(self.limit)

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

Um dann eine Funktion mit einem benutzerdefinierten Limit aufzurufen, können Sie Folgendes tun:

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

Beim Austritt aus dem Körper der with -Anweisung wird das Rekursionslimit auf den Standardwert zurückgesetzt.

PS Möglicherweise möchten Sie auch die Stapelgröße des Python-Prozesses für große Werte des Rekursionslimits erhöhen. Das geht über die ulimit Shell eingebaut oder limits.conf(5) Datei zum Beispiel.

Benutzer-Avatar
Ciro Santilli Путлер Капут 六四事

resource.setrlimit muss auch verwendet werden, um die Stapelgröße zu erhöhen und Segfault zu verhindern

Der Linux-Kernel begrenzt den Stapel von Prozessen.

Python speichert lokale Variablen auf dem Stack des Interpreters, sodass die Rekursion Stack-Speicherplatz des Interpreters beansprucht.

Wenn der Python-Interpreter versucht, das Stack-Limit zu überschreiten, macht der Linux-Kernel einen Segmentierungsfehler.

Die Stack-Limit-Größe wird mit gesteuert getrlimit und setrlimit Systemaufrufe.

Python bietet Zugriff auf diese Systemaufrufe über die resource Modul.

sys.setrecursionlimit B. unter https://stackoverflow.com/a/3323013/895245 erwähnt, erhöht nur die Grenze, die der Python-Interpreter selbst seiner eigenen Stapelgröße auferlegt, berührt aber nicht die Grenze, die der Linux-Kernel dem Python-Prozess auferlegt.

Beispielprogramm:

main.py

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)

Natürlich, wenn Sie weiter zunehmen setrlimitwird Ihr RAM irgendwann knapp, was entweder Ihren Computer aufgrund von Swap-Wahnsinn zum Stillstand bringt oder Python über den OOM Killer tötet.

Von bash aus können Sie das Stack-Limit (in kb) sehen und festlegen mit:

ulimit -s
ulimit -s 10000

Der Standardwert für mich ist 8 MB.

Siehe auch:

  • Stapelgröße in einem Python-Skript festlegen
  • Was ist das Hard-Recursion-Limit für Linux, Mac und Windows?

Getestet auf Ubuntu 16.10, Python 2.7.12.

  • Versuch zu setzen rlimit_stack nach Stapelkonflikt Korrekturen können zu Fehlern oder ähnlichen Problemen führen. Siehe auch RedHat Ausgabe 1463241

    – jww

    21. Juni 2017 um 16:35 Uhr

  • Ich habe dies (den Python-Ressourcenteil) verwendet, um meine Implementierung des Kosaraju-Algorithmus auf dem mittleren (riesigen) Datensatz von Professor Tim Roughgarden zu unterstützen. Meine Implementierung funktionierte bei kleinen Sets, sicherlich war das Problem bei einem großen Dataset das Rekursions-/Stack-Limit … Oder war es das? Nun, ja, das war es! Vielen Dank!

    – Nilo

    28. Januar 2019 um 8:04 Uhr


  • Danke Ciro tolle Details Antwort!

    – X0-Benutzer-0X

    26. Juni um 15:23 Uhr

Benutzer-Avatar
Marcelo Cantos

Verwenden Sie eine Sprache, die Tail-Call-Optimierung garantiert. Oder verwenden Sie die Iteration. Alternativ werden Sie süß mit Dekorateure.

  • Versuch zu setzen rlimit_stack nach Stapelkonflikt Korrekturen können zu Fehlern oder ähnlichen Problemen führen. Siehe auch RedHat Ausgabe 1463241

    – jww

    21. Juni 2017 um 16:35 Uhr

  • Ich habe dies (den Python-Ressourcenteil) verwendet, um meine Implementierung des Kosaraju-Algorithmus auf dem mittleren (riesigen) Datensatz von Professor Tim Roughgarden zu unterstützen. Meine Implementierung funktionierte bei kleinen Sets, sicherlich war das Problem bei einem großen Dataset das Rekursions-/Stack-Limit … Oder war es das? Nun, ja, das war es! Vielen Dank!

    – Nilo

    28. Januar 2019 um 8:04 Uhr


  • Danke Ciro tolle Details Antwort!

    – X0-Benutzer-0X

    26. Juni um 15:23 Uhr

Benutzer-Avatar
Daniel

Mir ist klar, dass dies eine alte Frage ist, aber für diejenigen, die lesen, würde ich davon abraten, Rekursion für Probleme wie diese zu verwenden – Listen sind viel schneller und vermeiden Rekursion vollständig. Ich würde dies wie folgt implementieren:

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

(Verwenden Sie n+1 in xrange, wenn Sie beginnen, Ihre Fibonacci-Folge bei 0 statt bei 1 zu zählen.)

  • Warum O(n)-Raum verwenden, wenn Sie O(1) verwenden können?

    – Janus Troelsen

    12. März 2014 um 9:11 Uhr

  • Nur für den Fall, dass der O(n)-Leerzeichen-Kommentar verwirrend war: Verwenden Sie keine Liste. List behält alle Werte, wenn Sie nur den n-ten Wert benötigen. Ein einfacher Algorithmus wäre, die letzten beiden Fibonacci-Zahlen beizubehalten und sie zu addieren, bis Sie die gewünschte erhalten. Es gibt auch bessere Algorithmen.

    – Milimetrisch

    14. Juli 2014 um 19:12 Uhr

  • @ Mathim: xrange heißt einfach rangein Python3.

    – Eric O. Lebigot

    3. August 2016 um 9:50 Uhr

  • @EOL Das ist mir bewusst

    – Mathim

    3. August 2016 um 9:51 Uhr

  • @Mathime Ich habe die Dinge für diejenigen, die diese Kommentare lesen, explizit gemacht.

    – Eric O. Lebigot

    3. August 2016 um 9:54 Uhr

1127310cookie-checkWas ist die maximale Rekursionstiefe in Python und wie kann sie erhöht werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy