Ressourcen zur Verbesserung Ihres Rekursionsverständnisses? [geschlossen]


13

Ich weiß, was Rekursion ist (wenn ein Patten in sich selbst wieder auftritt, normalerweise eine Funktion, die sich nach einem ausbruchbedingten ... richtig? Auf einer seiner Linien aufruft), und ich kann rekursive Funktionen verstehen, wenn ich sie genau studiere. Mein Problem ist, wenn ich neue Beispiele sehe, bin ich anfangs immer verwirrt. Wenn ich eine Schleife oder ein Mapping, Zippen, Verschachteln, polymorphes Aufrufen usw. sehe, weiß ich, was passiert, wenn ich es mir nur ansehe. Wenn ich rekursiven Code sehe, lautet mein Gedankenprozess normalerweise "wtf is this?" gefolgt von 'oh, es ist rekursiv' gefolgt von 'Ich denke, es muss funktionieren, wenn sie sagen, dass es funktioniert.'

Haben Sie also Tipps / Pläne / Ressourcen zum Aufbau von Fähigkeiten in diesem Bereich? Rekursion ist eine Art seltsames Konzept, daher denke ich, dass die Art und Weise, wie es angegangen werden soll, ebenso seltsam und unauffällig sein kann.


28
Um die Rekursion zu verstehen, müssen Sie zuerst die Rekursion verstehen.
Andreas Johansson

1
"Die Katze mit dem Hut kommt zurück" von Dr. Seuss, dies mag nicht ganz nützlich sein, aber der rekursive Aufruf an die Katze beseitigt diesen lästigen Fleck. :-) Es hat auch den Vorteil, sehr schnell gelesen zu werden!
DKnight

2
Üben, üben, üben.
David Thornley

20
In dieser Frage bereits beantwortet: programmers.stackexchange.com/questions/57243/…
Graham Borland

3
@ Abraham Borland: Das ist ein Beispiel für unendliche Rekursion. In den meisten Programmen führt das Fehlen des Basisfalls normalerweise zu einem Stapelüberlauf oder zu einem Fehler aufgrund von Speichermangel. Für Website-Benutzer kann dies nur zu Verwirrung führen. ;)
FrustratedWithFormsDesigner

Antworten:


10

Beginnen Sie mit etwas Einfachem und zeichnen Sie es mit Bleistift und Papier nach. Ernsthaft. Ein guter Ausgangspunkt sind Tree-Traversal-Algorithmen, da sie mithilfe von Rekursion viel einfacher zu handhaben sind als reguläre Iterationen. Es muss kein kompliziertes Beispiel sein, sondern etwas, das einfach ist und mit dem Sie arbeiten können.

Ja, es ist seltsam und manchmal kontraintuitiv, aber sobald es klickt, sagt man "Eureka!" du wirst dich fragen, wie du es vorher nicht verstanden hast ! ;) Ich schlug Bäume vor, weil sie (IMO) die am einfachsten zu verstehende Struktur in der Rekursion sind und mit Bleistift und Papier leicht zu bearbeiten sind. ;)


1
+1 So habe ich es gemacht. Beispiel: Wenn Sie OO verwenden, erstellen Sie einige Klassen mit einer übergeordneten untergeordneten Beziehung und versuchen Sie dann, eine Funktion / Methode zu erstellen, die prüft, ob ein Objekt einen bestimmten Vorfahren hat.
Alb

5

Ich empfehle Scheme mit dem Buch The Little Lisper. Sobald Sie damit fertig sind, werden Sie die Rekursion tief im Inneren verstehen. Fast garantiert.


1
+1 Dieses Buch hat es wirklich für mich getan. Aber es wurde umbenannt in "The Little Schemer"
mike30

4

Ich empfehle auf jeden Fall SICP. Schauen Sie sich auch die Einführungsvideos der Autoren an . Sie sind unglaublich aufschlussreich.

Ein anderer Weg, der nicht so eng mit dem Programmieren zusammenhängt, ist das Lesen von Gödel, Escher, Bach: Ein ewiger goldener Zopf von Hofstadter. Sobald Sie damit fertig sind, sieht die Rekursion so natürlich aus wie die Arithmetik. Sie werden auch überzeugt sein, dass P = nP und Sie werden Denkmaschinen bauen wollen - aber es ist ein Nebeneffekt, der im Vergleich zu den Vorteilen so gering ist.


GEB ist trotzdem lesenswert; Auch wenn einige der Dinge, über die er spricht, ein wenig veraltet sind (in den letzten 40 Jahren wurden einige Fortschritte in der CS-Grundlagenforschung erzielt), ist das Grundverständnis nicht so.
Donal Fellows

2

Im Wesentlichen kommt es nur auf die Übung an ... Nehmen Sie allgemeine Probleme (Sortieren, Suchen, mathematische Probleme usw.) und sehen Sie, wie diese Probleme gelöst werden können, wenn Sie eine einzelne Funktion mehrmals anwenden.

Die schnelle Sortierung verschiebt beispielsweise ein Element in einer Liste in zwei Hälften und wendet sich dann erneut auf jede dieser Hälften an. Wenn die anfängliche Sortierung stattfindet, ist es nicht wichtig, die beiden Hälften an diesem Punkt zu sortieren. Vielmehr nimmt man das Pivot-Element und legt alle Elemente, die kleiner als dieses Element sind, auf eine Seite und alle Elemente, die größer oder gleich sind, auf die andere Seite. Ist es sinnvoll, wie es sich an diesem Punkt rekursiv selbst aufrufen kann, um die beiden neuen kleineren Listen zu sortieren? Das sind auch Listen. Einfach kleiner. Aber sie müssen noch sortiert werden.

Die Kraft hinter der Rekursion ist die Idee der Teilung und Überwindung. Teilen Sie ein Problem wiederholt in kleinere Probleme auf, die von Natur aus identisch, aber nur kleiner sind. Wenn du das genug tust, kommst du irgendwann zu einem Punkt, an dem das einzige verbleibende Stück bereits gelöst ist, und du arbeitest dich einfach wieder aus der Schleife heraus, und das Problem ist gelöst. Studieren Sie die Beispiele, die Sie erwähnt haben, bis Sie sie verstanden haben. Es kann eine Weile dauern, aber irgendwann wird es einfacher. Versuchen Sie dann, andere Probleme aufzugreifen und eine rekursive Funktion zu erstellen, um sie zu lösen! Viel Glück!

EDIT: Ich muss auch hinzufügen, dass ein Schlüsselelement für die Rekursion die garantierte Fähigkeit der Funktion ist, stoppen zu können. Dies bedeutet, dass die Zerlegung des ursprünglichen Problems kontinuierlich kleiner werden muss und schließlich ein garantierter Haltepunkt vorhanden sein muss (ein Punkt, an dem das neue Teilproblem entweder lösbar oder bereits gelöst ist).


Ja, ich glaube, ich habe schon früher eine kurze Erklärung für das Sortieren gesehen. Ich kann mir vorstellen, wie es funktioniert. Wie expressiv / flexibel ist Rekursion - können die meisten Probleme zu einem rekursiven Ansatz gezwungen werden (auch wenn dieser nicht optimal ist)? Ich habe Leute gesehen, die Codierungsrätsel im Netz beantworteten, die die meisten Leute prozedural anpackten, als könnten sie Rekursion verwenden, wann immer sie wollten, nur zum Teufel. Ich habe auch einmal gelesen, dass einige Sprachen abhängig oder rekursiv sind, um das Schleifenkonstrukt zu ersetzen. Und Sie erwähnen den garantierten Haltepunkt. Ich glaube, eines dieser Dinge könnte der Schlüssel sein.
Andrew M

Ein gutes einfaches Startproblem für Sie wäre, ein rekursives Programm zu schreiben, das die Fakultät einer Zahl findet.
Kenneth

Jede Schleifenstruktur kann in eine rekursive Struktur eingefügt werden. Jede rekursive Struktur kann in eine Schleifenstruktur eingefügt werden ... mehr oder weniger. Es braucht Zeit und Übung, um zu lernen, wann und wann Rekursion nicht verwendet werden soll, da Sie sich daran erinnern müssen, dass bei der Verwendung von Rekursion viel Aufwand in Bezug auf die auf Hardwareebene verwendeten Ressourcen anfällt.
Kenneth

Zum Beispiel könnte ich sehen, dass es machbar ist, eine Schleifenstruktur zu erstellen, die eine schnelle Sortierung durchführt ... ABER es wäre sicher ein königlicher Schmerz, und je nachdem, wie es gemacht wurde, könnte es am Ende mehr Systemressourcen verbrauchen als eine rekursive Funktion für große Arrays.
Kenneth

Also hier ist mein Versuch der Fakultät. um fair zu sein habe ich das schon mal gesehen und obwohl ich es von Grund auf geschrieben habe, nicht aus dem Gedächtnis, ist es wahrscheinlich immer noch einfacher als es gewesen wäre. Versuchte es in JS, hatte aber einen Analysefehler, arbeitet aber in Python def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Andrew M

2

Persönlich denke ich, dass Ihre beste Wette durch Übung ist.

Ich habe mit LOGO Rekursion gelernt. Sie könnten LISP verwenden. Rekursion ist in diesen Sprachen selbstverständlich. Ansonsten können Sie es mit dem Studium von mathematischen Reihen und Reihen vergleichen, in denen Sie ausdrücken, was als nächstes basierend auf dem Vorherigen kommt, dh u (n + 1) = f (u (n)), oder mit komplexeren Reihen, in denen Sie mehrere Variablen und haben multiple Abhängigkeiten, zB u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...

Mein Vorschlag wäre also, dass Sie einfache (in ihrem Ausdruck) Standardrekursions- "Probleme" finden und versuchen, sie in der Sprache Ihrer Wahl zu implementieren. Übung wird Ihnen helfen, zu lernen, wie Sie diese "Probleme" denken, lesen und ausdrücken. Beachten Sie, dass einige dieser Probleme häufig durch Iteration ausgedrückt werden können, eine Rekursion jedoch möglicherweise eine elegantere Methode zur Lösung darstellt. Eine davon ist die Berechnung von Fakultätszahlen.

Grafische "Probleme", die ich finde, erleichtern das Erkennen. Schauen Sie sich also Kochs Flocken, Fibonacci, die Drachenkurve und Fraktale im Allgemeinen an. Schauen Sie sich aber auch den Schnell-Sortier-Algorithmus an ...

Sie müssen ein paar Programme zum Absturz bringen (Endlosschleifen, versuchsweise Verwendung unendlicher Ressourcen) und die Endbedingungen falsch handhaben (um unerwartete Ergebnisse zu erzielen), bevor Sie sich mit all dem befassen. Und selbst wenn Sie es bekommen, werden Sie diese Fehler immer noch machen, nur seltener.


2

Struktur und Interpretation von Computerprogrammen

Es ist das Buch, das zum Lernen, nicht nur zur Rekursion, sondern allgemein zum Programmieren an verschiedenen Universitäten verwendet wird. Es ist eines dieser grundlegenden Bücher, die mehr Informationen liefern, je mehr Erfahrung Sie sammeln und je mehr Sie lesen.


0

So sehr ich SICP und Gödel, Escher, Bach: ein ewiges goldenes Geflecht mag , macht Touretzkys LISP: Eine sanfte Einführung in die symbolische Berechnung auch einen guten Job bei der Einführung der Rekursion.

Das Grundkonzept lautet wie folgt: Zuerst müssen Sie wissen, wann Ihre rekursive Funktion beendet ist, damit ein Ergebnis zurückgegeben werden kann. Dann müssen Sie wissen, wie Sie den unvollendeten Fall auf etwas reduzieren, auf das Sie zurückgreifen können. Für das traditionelle Beispiel der Fakultät (N) sind Sie fertig, wenn N <= 1 ist, und der unfertige Fall ist N * Fakultät (N-1).

Für ein viel hässlicheres Beispiel gibt es Ackermanns Funktion A (m, n).

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.

0

Ich schlage vor, mit einigen funktionalen ML-Sprachen wie OCaml oder Haskell herumzuspielen. Ich fand, dass die Syntax der Mustererkennung mir wirklich half, auch relativ komplizierte rekursive Funktionen zu verstehen, sicherlich viel besser als die von Scheme ifund condAnweisungen. (Ich habe Haskell und Schema gleichzeitig gelernt.)

Hier ist ein triviales Beispiel für Kontrast:

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

und mit Pattern Matching:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

Dieses Beispiel wird dem Unterschied nicht wirklich gerecht - ich hatte nie Probleme mit einer der beiden Versionen der Funktion. Es soll nur veranschaulichen, wie die beiden Optionen aussehen. Sobald Sie mit Listen und Bäumen zu komplexeren Funktionen gelangen, wird der Unterschied noch deutlicher.

Ich empfehle Haskell besonders, weil es eine einfache Sprache mit sehr guter Syntax ist. Es ist auch viel einfacher, mit fortgeschritteneren Ideen wie Corecursion zu arbeiten :

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(Sie werden den obigen Code erst verstehen, wenn Sie ein wenig mit Haskell spielen, aber seien Sie versichert, dass er im Grunde magisch ist: P.) Natürlich können Sie dasselbe mit Streams in Scheme tun, aber es ist in Haskell viel natürlicher.


0

Es ist vergriffen, aber wenn Sie es finden können, handelt es sich bei "Recursive Algorithms" von Richard Lorentz nur um eine Rekursion. Es behandelt die Grundlagen der Rekursion sowie spezifische rekursive Algorithmen.

Die Beispiele sind in Pascal, aber nicht so groß, dass die Wahl der Sprache lästig ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.