Manchmal verwende ich in Interviews die Rekursion, um ein Problem zu lösen (z. B. das Hinzufügen 1
zu einer Ganzzahl mit unendlicher Genauigkeit), oder wenn sich das Problem als geeignet für die Verwendung der Rekursion herausstellt. Manchmal kann es nur daran liegen, dass Rekursion häufig zur Problemlösung verwendet wird. Ohne viel nachzudenken, wird Rekursion verwendet, um das Problem zu lösen.
Was sind jedoch die Überlegungen, bevor Sie entscheiden können, ob die Rekursion zur Lösung eines Problems geeignet ist?
Einige Gedanken, die ich hatte:
Wenn wir die Rekursion für Daten verwenden, die jedes Mal halbiert werden, scheint es kein Problem zu sein, die Rekursion zu verwenden, da alle Daten, die in 16 GB RAM oder sogar eine 8-TB-Festplatte passen, durch eine Rekursion mit einer Tiefe von nur 42 Ebenen verarbeitet werden können. (also kein Stapelüberlauf (Ich denke, in einigen Umgebungen kann der Stapel 4000 Ebenen tief sein, weit mehr als 42, aber gleichzeitig hängt es auch davon ab, wie viele lokale Variablen Sie haben, da jeder Aufrufstapel mehr Speicher belegt Wenn es viele lokale Variablen gibt und es die Speichergröße und nicht die Ebene ist, die den Stapelüberlauf bestimmt)).
Wenn Sie Fibonacci-Zahlen mit reiner Rekursion berechnen, müssen Sie sich wirklich um die zeitliche Komplexität kümmern, es sei denn, Sie zwischenspeichern die Zwischenergebnisse.
Und wie wäre es 1
mit einer Ganzzahl mit unendlicher Genauigkeit? Vielleicht ist es fraglich, ob Sie mit Zahlen arbeiten, die 3000 oder 4000 Stellen lang sind und so groß, dass es zu einem Stapelüberlauf kommen kann. Ich habe nicht daran gedacht, aber vielleicht lautet die Antwort nein, wir sollten keine Rekursion verwenden, sondern nur eine einfache Schleife, denn was ist, wenn in einer Anwendung die Zahl wirklich 4000 Stellen lang sein muss, um nach einigen zu suchen? Eigenschaften der Zahl, z. B. ob die Zahl eine Primzahl ist oder nicht.
Die letzte Frage lautet: Was sind die Überlegungen, bevor Sie sich für die Rekursion zur Lösung eines Problems entscheiden können?
quelle
1
einer Ganzzahl mit unendlicher Genauigkeit? Sie können sagen, ja, sie reduzieren sich auf ein kleineres Problem, aber reine Rekursion ist nicht dafür geeignetAntworten:
Eine Überlegung ist, ob Ihr Algorithmus eine abstrakte Lösung oder eine praktische ausführbare Lösung sein soll. Im ersteren Fall sind die Attribute, nach denen Sie suchen, Korrektheit und Verständnis für Ihre Zielgruppe 1 . Im letzteren Fall ist auch die Leistung ein Problem. Diese Überlegungen können Ihre Wahl beeinflussen.
Eine zweite Überlegung (für eine praktische Lösung) ist, ob die von Ihnen verwendete Programmiersprache (oder genauer gesagt ihre Implementierung) die Eliminierung von Tail-Calls bewirkt. Ohne Eliminierung von Tail-Calls ist die Rekursion langsamer als die Iteration, und eine tiefe Rekursion kann zu Problemen mit dem Stapelüberlauf führen.
Beachten Sie, dass eine (richtige) rekursive Lösung wird umgewandelt in eine äquivalente nicht-rekursive Lösung, so dass Sie nicht unbedingt eine harte Wahl zwischen den beiden Ansätzen müssen machen.
Schließlich wird die Wahl zwischen rekursiven und nicht rekursiven Formulierungen manchmal durch die Notwendigkeit motiviert, (im formalen Sinne) Eigenschaften eines Algorithmus zu beweisen. Rekursive Formulierungen ermöglichen direkter den Nachweis durch Induktion.
1 - Dies beinhaltet Überlegungen, ob die Zielgruppe ... und dies könnte Programmierer beinhalten, die praktischen Code lesen ... einen Lösungsstil als "natürlicher" als den anderen ansehen würden. Der Begriff "natürlich" variiert von Person zu Person, je nachdem, wie sie Programmierung oder Algorithmus gelernt haben. (I Herausforderung jeden, der „Natürlichkeit“ , wie die schlägt primäre Kriterien für die Entscheidung zur Verwendung Rekursion (oder nicht) zu definieren „Natürlichkeit“ objektiv, das heißt , wie man es messen würde.)
quelle
Als C / C ++ - Programmierer ist die Leistung meine wichtigste Überlegung. Mein Entscheidungsprozess ist ungefähr so:
Was ist die maximale Tiefe des Aufrufstapels? Wenn zu tief, entfernen Sie die Rekursion. Wenn flach, gehen Sie zu 2.
Ist diese Funktion wahrscheinlich ein Engpass in meinem Programm? Wenn ja, fahren Sie mit Schritt 3 fort. Wenn nein, behalten Sie die Rekursion bei. Wenn Sie sich nicht sicher sind, führen Sie einen Profiler aus.
Wie viel Prozent der CPU-Zeit wird für rekursive Funktionsaufrufe aufgewendet? Wenn Funktionsaufrufe erheblich weniger Zeit in Anspruch nehmen als der Rest des Funktionskörpers, kann die Rekursion verwendet werden.
quelle
Wenn ich Funktionen in Schema schreibe, finde ich es natürlich, rekursive Schwanzfunktionen zu schreiben, ohne zu viel nachzudenken.
Wenn ich Funktionen in C ++ schreibe, diskutiere ich, bevor ich eine rekursive Funktion verwende. Die Fragen, die ich mir stelle, sind:
Kann die Berechnung mit einem iterativen Algorithmus durchgeführt werden? Wenn ja, verwenden Sie einen iterativen Ansatz.
Kann die Rekursionstiefe um die Größe des Modells zunehmen? Ich bin kürzlich auf einen Fall gestoßen, in dem die Rekursionstiefe aufgrund der Größe des Modells auf fast 13000 angewachsen ist. Ich musste die Funktion konvertieren, um nach der Eile einen iterativen Algorithmus zu verwenden.
Aus diesem Grund würde ich nicht empfehlen, einen Tree-Traversal-Algorithmus mit rekursiven Funktionen zu schreiben. Sie wissen nie, wann der Baum für Ihre Laufzeitumgebung zu tief wird.
Kann die Funktion durch Verwendung eines iterativen Algorithmus zu kompliziert werden? Wenn ja, verwenden Sie eine rekursive Funktion. Ich habe nicht versucht,
qsort
mit einem iterativen Ansatz zu schreiben , aber ich habe das Gefühl, dass die Verwendung einer rekursiven Funktion für sie natürlicher ist.quelle
Für Fibonacci-Zahlen ist die naive "Rekursion" einfach total dumm. Das liegt daran, dass das gleiche Teilproblem immer wieder gelöst wird.
Es gibt tatsächlich eine triviale Variation der Fibonacci-Zahlen, bei der die Rekursion sehr effizient ist: Berechnen Sie bei einer Zahl n ≥ 1 sowohl fib (n) als auch fib (n-1). Sie benötigen also eine Funktion, die zwei Ergebnisse zurückgibt. Rufen Sie diese Funktion fib2 auf.
Die Implementierung ist ganz einfach:
quelle
fib2
ein Zahlenpaar zurück, und Siefib2()
passen nicht zur Schnittstelle vonfib()
, die bei einer gegebenen Zahl eine Zahl zurückgibt . Es scheint, dass Siefib(n)
zurückkehren sollen,fib2(n)[0]
aber bitte seien Sie genau