Was ist die maximale Rekursionstiefe in Python und wie kann sie erhöht werden?
Lesezeit: 8 Minuten
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
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:
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.
Aus meiner Erfahrung müssen Sie das Limit sowohl in der erhöhen sys und die resource Module: stackoverflow.com/a/16248113/205521
Schwanzrekursion ist eine vollkommen effiziente Technik in einer dafür optimierten Programmiersprache. Für die richtige Art von Problem kann es wesentlich aussagekräftiger sein als eine iterative Implementierung. Die Antwort bedeutet wahrscheinlich “speziell in Python”, aber das ist nicht das, was sie sagt
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
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.
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
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:
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.
Ciro Santilli Путлер Капут 六四事
resource.setrlimit muss auch verwendet werden, um die Stapelgröße zu erhöhen und Segfault zu verhindern
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
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
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
11273100cookie-checkWas ist die maximale Rekursionstiefe in Python und wie kann sie erhöht werden?yes
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ürn=1
(weil der Basisfall istn < 1
so fürn=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 es998 + 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 an998
. Wenn du anrufstrecursive_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