711 Stimmen

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

Ich habe diese Endrekursive 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 n=997, dann bricht es einfach ab und gibt einen RecursionError: maximum recursion depth exceeded in comparison aus. Ist das einfach ein Stacküberlauf? Gibt es einen Weg, das zu umgehen?

4 Stimmen

21 Stimmen

Memoisierung könnte die Geschwindigkeit Ihrer Funktion steigern und ihre effektive rekursive Tiefe erhöhen, indem zuvor berechnete Werte beendet werden, anstatt die Stapelgröße zu erhöhen.

6 Stimmen

Die Rekursionsobergrenze beträgt in der Regel 1000.

812voto

Thomas Wouters Punkte 124421

Es ist ein Schutz gegen einen Stack-Überlauf, ja. Python (bzw. die CPython-Implementierung) optimiert keine Endrekursion und ungebremste Rekursion führt zu Stack-Überläufen. Du kannst das Rekursionslimit mit sys.getrecursionlimit überprüfen:

import sys
print(sys.getrecursionlimit())

und das Rekursionslimit mit sys.setrecursionlimit ändern:

sys.setrecursionlimit(1500)

aber das ist gefährlich - das Standardlimit ist etwas konservativ, aber Python-Stackframes können ziemlich groß sein.

Python ist keine funktionale Sprache und Endrekursion ist keine besonders effiziente Technik. Das Algorithmus umzuschreiben, falls möglich, ist im Allgemeinen eine bessere Idee.

15 Stimmen

Aus meiner Erfahrung müssen Sie das Limit sowohl in den sys als auch in den resource Modulen erhöhen: stackoverflow.com/a/16248113/205521

5 Stimmen

Als Taktik, um es in eine iterative Version umzuwandeln, könnte ein Tail-Call-Optimierungsdekorator verwendet werden

5 Stimmen

Sie können svn.python.org/projects/python/trunk/Tools/scripts/… verwenden, um Ihr Betriebssystem-Obergrenze herauszufinden.

184voto

David Young Punkte 4493

Es sieht so aus, als müssten Sie einfach eine höhere Rekursionstiefe festlegen:

import sys
sys.setrecursionlimit(1500)

1 Stimmen

In meinem Fall habe ich die Rückgabeanweisung im Basisfall vergessen und sie ging über 1000 hinaus. Python hat diese Ausnahme ausgelöst und ich war erstaunt, weil ich mir sicher war, wie viele Stapel es erstellen wird, um es auszuführen.

9 Stimmen

Sys.setrecursionlimit(50) oder eine kleine Menge ist nützlich, wenn Ihr Programm in Rekursion eintritt und Sie möchten, dass die Fehlermeldung NICHT Seiten und Seiten des gleichen Textes sind. Ich fand dies sehr hilfreich beim Debuggen meines schlechten rekursiven Codes.

0 Stimmen

Bitte beachten Sie, dass dies von plattformabhängigen Grenzen abhängig ist.

85voto

Eugene Yarmash Punkte 130008

Wenn Sie häufig den Rekursionslimit ändern müssen (z. B. beim Lösen von Programmieraufgaben), können Sie einen einfachen Kontext-Manager wie folgt definieren:

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)

Dann können Sie eine Funktion mit einem benutzerdefinierten Limit aufrufen:

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

Beim Verlassen des with-Blocks wird das Rekursionslimit auf den Standardwert zurückgesetzt.

P.S. Sie möchten vielleicht auch die Stackgröße des Python-Prozesses für hohe Werte des Rekursionslimits erhöhen. Das kann über das Shell-Builtin ulimit oder die Datei limits.conf(5) erfolgen, zum Beispiel.

3 Stimmen

Sie wollen auch den Rekursionslimit des Prozesses mit Ressource erhöhen. Ohne dies erhalten Sie einen Segmentation Fault und der gesamte Python-Prozess stürzt ab, wenn Sie setrecursionlimit zu hoch setzen und versuchen, das neue Limit zu verwenden (ungefähr 8 Megabyte Stackframes, was ungefähr ~30.000 Stackframes mit der einfachen Funktion auf meinem Laptop entspricht).

0 Stimmen

@Boris: Das könnte dem Kontext-Manager hinzugefügt werden, aber die Erhöhung des Stapelgrößenlimits funktioniert nur für den Root-Benutzer.

70voto

Scharron Punkte 16533

Es dient zur Vermeidung eines Stapelüberlaufs. Der Python-Interpreter begrenzt die Rekursionstiefe, um zu verhindern, dass Sie unendliche Rekursionen erhalten, die zu Stapelüberläufen führen. Versuchen Sie, das Rekursionslimit zu erhöhen (sys.setrecursionlimit) oder Ihren Code ohne Rekursion neu zu schreiben.

Aus der Python-Dokumentation:

sys.getrecursionlimit()

Gibt den aktuellen Wert des Rekursionslimits zurück, die maximale Tiefe des Python-Interpreter-Stapels. Dieses Limit verhindert, dass unendliche Rekursionen einen Überlauf des C-Stapels verursachen und Python abstürzen lassen. Es kann durch setrecursionlimit() gesetzt werden.

1 Stimmen

Auf meinem Anaconda x64, 3.5 Python auf Windows beträgt das Standardlimit 1000.

25voto

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

Der Linux-Kernel beschränkt den Stapel von Prozessen.

Python speichert lokale Variablen auf dem Stapel des Interpreters, und so nimmt die Rekursion den Stapelspeicher des Interpreters ein.

Wenn der Python-Interpreter versucht, das Stapellimit zu überschreiten, bewirkt der Linux-Kernel einen Segmentationsfehler.

Die Stapellimitgröße wird mit den Systemaufrufen getrlimit und setrlimit kontrolliert.

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

sys.setrecursionlimit erwähnt z.B. unter https://stackoverflow.com/a/3323013/895245 erhöht nur das Limit, das der Python-Interpreter selbst auf seine eigene Stapelgröße auferlegt, berührt aber nicht das Limit, das der Linux-Kernel dem Python-Prozess auferlegt.

Beispielsprogramm:

main.py

import resource
import sys

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

# Würde ohne diese Zeile Segfault verursachen.
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 weiterhin setrlimit erhöhen, wird Ihr RAM irgendwann ausgehen, was entweder Ihren Computer aufgrund des Austausch-Irrsinns zum Stillstand bringen oder Python über den OOM-Killer beenden wird.

Von der Bash aus können Sie das Stapellimit (in kb) sehen und setzen mit:

ulimit -s
ulimit -s 10000

Der Standardwert für mich beträgt 8 MB.

Siehe auch:

Getestet auf Ubuntu 16.10, Python 2.7.12.

2 Stimmen

Der Versuch, rlimit_stack nach den Behebungen von Stack Clash zu setzen, kann zu einem Ausfall oder verwandten Problemen führen. Siehe auch Red Hat Problem 1463241

1 Stimmen

Ich habe diesen (den Python-Ressourceteil) verwendet, um meine Implementierung des Kosaraju-Algorithmus auf Professor Tim Roughgardens mittleren (riesigen) Datensatz zu helfen. Meine Implementierung funktionierte bei kleinen Datensätzen, sicherlich war das Problem bei einem großen Datensatz die Rekursion/Stack-Grenze ... Oder war es das? Nun, ja, das war es! Danke!

1 Stimmen

Vielen Dank, Ciro, für die ausführliche Antwort!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X