Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Problemlösen mit Rekursion

Ähnliche Präsentationen


Präsentation zum Thema: "Problemlösen mit Rekursion"—  Präsentation transkript:

1 Problemlösen mit Rekursion
Klaus Becker 2009

2 Problemlösen mit Rekursion
Inhalte: Problemlösen durch Problemreduktion Selbstähnliche Figuren Rekursive Verarbeitung von Listen Rekursive Verarbeitung natürlicher Zahlen Rekursion und Berechnungsaufwand Rekursion und Iteration

3 Problemlösen durch Problemreduktion
Teil 1 Problemlösen durch Problemreduktion

4 Einstieg - Türme von Hanoi
Einer Geschichte zufolge soll im Tempel zu Benares - das ist eine "heilige Stadt" in Indien - ein Turm aus 64 goldenen, der Größe nach geordneten Scheiben stehen. Die Mönche des Tempels erhalten die Aufgabe, die Scheiben an einen anderen Ort zu bringen. Dabei müssen sie einige Regeln beachten: Es darf immer nur eine Scheibe transportiert werden. Scheiben können auf einem (einzigen) Hilfsstapel zwischenzeitlich abgelegt werden. Auch auf dem (teilweise abgebauten) Ausgangsturm können Scheiben zwischenzeitlich abgelegt werden. Es darf aber nie eine größere Scheibe auf eine kleinere gelegt werden. Wenn der neue Turm fertig ist, dann ist das Ende der Zeit erreicht.

5 Einstieg - Aufgabe (siehe 9.1.1)
Versuchen Sie, einen Turm mit 5 Scheiben nach den vorgegebenen Regeln umzustapeln. Wenn das nicht klappt, dann versuchen Sie erst einmal, Türme mit 3 bzw. 4 Scheiben umzustapeln. Ausgangszustand Zielzustand Benutzen Sie Münzen unterschiedlicher Größe oder ein Simulationsprogramm. z. B.:

6 Einstieg - Aufgabe (siehe 9.1.1)
Überlegen Sie sich auch eine Strategie, mit der man Türme mit 6, 7, ... Scheiben umstapeln kann. Ausgangszustand Zielzustand

7 Lösungsidee transportiere einen 5-Scheiben-Turm von A über B nach C
Ausgangszustand transportiere einen 4-Scheiben-Turm von A über C nach B Zwischenzustand transportiere eine Scheibe von A nach C Zwischenzustand transportiere einen 4-Scheiben-Turm von B über A nach C Zielzustand

8 Verallgemeinerung transportiere einen n-Scheiben-Turm von X über Y nach Z Ausgangszustand transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y Zwischenzustand transportiere eine Scheibe von X nach Z Zwischenzustand transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z Zielzustand

9 Algorithmus Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst:

10 Rekursive Problemreduktion
Rekursive Problemreduktion ist eine Problemlösestrategie, bei der ein Problem auf ein strukturgleiches Problem (in verkleinerter Form) zurückgeführt wird. Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst: Ein rekursiver Algorithmus ruft sich (eventuell über Umwege) selbst auf und nutzt sich so selbst zur Beschreibung der Lösung des gegebenen Problems. Um Rekursion als Problemlösestrategie nutzen zu können, benötigt man ein Ausführsystem, das in der Lage ist, rekursive Algorithmen wiederholt aufzurufen und auf diese Weise die eigentliche Lösung zu generieren.

11 Ausführung des Algorithmus
Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst: Ausführungstiefe: 1 transportiere einen 3-Scheiben-Turm von A über B nach C: transportiere einen 2-Scheiben-Turm von A über C nach B transportiere eine Scheibe von A nach C transportiere einen 2-Scheiben-Turm von B über A nach C

12 Ausführung des Algorithmus
Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst: Ausführungstiefe: 2 transportiere einen 3-Scheiben-Turm von A über B nach C: transportiere einen 2-Scheiben-Turm von A über C nach B: transportiere einen 1-Scheiben-Turm von A über B nach C transportiere eine Scheibe von A nach B transportiere einen 1-Scheiben-Turm von C über A nach B transportiere eine Scheibe von A nach C transportiere einen 2-Scheiben-Turm von B über A nach C: transportiere einen 1-Scheiben-Turm von B über C nach A transportiere eine Scheibe von B nach C

13 Ausführung des Algorithmus
Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst: Ausführungstiefe: 3 transportiere einen 3-Scheiben-Turm von A über B nach C: transportiere einen 2-Scheiben-Turm von A über C nach B: transportiere einen 1-Scheiben-Turm von A über B nach C: transportiere eine Scheibe von A nach C transportiere eine Scheibe von A nach B transportiere einen 1-Scheiben-Turm von C über A nach B: transportiere eine Scheibe von C nach B transportiere einen 2-Scheiben-Turm von B über A nach C: transportiere einen 1-Scheiben-Turm von B über C nach A: transportiere eine Scheibe von B nach A transportiere eine Scheibe von B nach C

14 Ausführung des Algorithmus
transportiere einen 3-Scheiben-Turm von A über B nach C: transportiere einen 2-Scheiben-Turm von A über C nach B: transportiere einen 1-Scheiben-Turm von A über B nach C: transportiere eine Scheibe von A nach C transportiere eine Scheibe von A nach B transportiere einen 1-Scheiben-Turm von C über A nach B: transportiere eine Scheibe von C nach B transportiere einen 2-Scheiben-Turm von B über A nach C: transportiere einen 1-Scheiben-Turm von B über C nach A: transportiere eine Scheibe von B nach A transportiere eine Scheibe von B nach C Basisaktionen

15 Implementierung in Python
Algorithmus: transportiere einen n-Scheiben-Turm von X über Y nach Z wenn n > 1: transportiere einen (n-1)-Scheiben-Turm von X über Z nach Y transportiere eine Scheibe von X nach Z transportiere einen (n-1)-Scheiben-Turm von Y über X nach Z sonst: Algorithmus def transportiereTurm(n, x, y, z): if n > 1: transportiereTurm(n-1, x, z, y) print "transportiere eine Scheibe von ", x, " nach ", z transportiereTurm(n-1, y, x, z) else: Python-Programm

16 Übungen (siehe 9.1.4) Bearbeiten Sie die Aufgaben 1, 2.

17 Selbstähnliche Figuren
Teil 2 Selbstähnliche Figuren

18 Einstieg - Selbstähnliche Figur
Eine Figur ist selbstähnlich, wenn sie sich in Teile zerlegen lässt, die zur ihr ähnlich sind.

19 Einstieg - Selbstähnliche Figur
Eine Figur ist selbstähnlich, wenn sie sich in Teile zerlegen lässt, die zur ihr ähnlich sind. zeichne_Baum(200): gehe_vorwaerts(200) drehe_dich_nach_rechts(45) zeichne_Baum(100) drehe_dich_nach_links(90) gehe_rueckwaerts(200) ALG zeichne_Baum(x): wenn x >-> 2: gehe_vorwaerts(x) drehe_dich_nach_rechts(45) zeichne_Baum(x/2) drehe_dich_nach_links(90) gehe_rueckwaerts(x) rekursive Problemreduktion rekursiver Algorithmus

20 Exkurs - Turtle-Grafik
Turtle-Grafik basiert auf der Vorstellung, dass eine Schildkröte mit bestimmten Anweisungen auf einer Zeichenfläche bewegt wird und dass die Schildkröte dabei eine Spur hinterlässt. vorwaerts(100) Turtle-Befehle zeichne_Quadrat(laenge): wiederhole 4 mal: gehe_vorwaerts(laenge) drehe_dich_nach_links(90) stift_hoch stift_runter gehe_vorwaerts(betrag) gehe_rueckwaerts(betrag) drehe_dich_nach_links(winkel) drehe_dich_nach_rechts(winkel) gehe_zu_punkt(punkt) ... Turtle-Algorithmus

21 Exkurs - Turtle-Grafik in Python
Turtle-Grafik basiert auf der Vorstellung, dass eine Schildkröte mit bestimmten Anweisungen auf einer Zeichenfläche bewegt wird und dass die Schildkröte dabei eine Spur hinterlässt. zeichne_Quadrat(laenge): wiederhole 4 mal: gehe_vorwaerts(laenge) drehe_dich_nach_links(90) stift_hoch stift_runter gehe_vorwaerts(betrag) gehe_rueckwaerts(betrag) drehe_dich_nach_links(winkel) drehe_dich_nach_rechts(winkel) gehe_zu_punkt(punkt) ... Turtle-Programm # -*- coding: iso *- from turtle import * # Deklaration einer Zeichenprozedur def quadrat(laenge): for i in range(4): t.forward(laenge) t.left(90) # Erzeugung eines Turtle-Objekts t = Turtle() # Test der Zeichenprozedur quadrat(100) Turtle-Klasse t.forward(100)

22 Exkurs - Turtle-Grafik in Python
# -*- coding: iso *- from turtle import * # Deklaration einer Zeichenprozedur def baum(stamm): if stamm >= 2: t.forward(stamm) t.right(45) baum(stamm/2) t.left(90) t.backward(stamm) # Erzeugung eines Turtle-Objekts t = Turtle() # Test der Zeichenprozedur baum(200) ALG zeichne_Baum(x): wenn x >= 2: gehe_vorwaerts(x) drehe_dich_nach_rechts(45) zeichne_Baum(x/2) drehe_dich_nach_links(90) gehe_rueckwaerts(x)

23 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

24 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

25 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

26 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

27 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

28 Übungen (siehe 9.2.3) Wählen Sie eine der folgenden selbstähnlichen Figuren aus. Entwickeln Sie mit Hilfe einer rekursive Problemreduktion einen rekursiven Algorithmus zum Zeichnen der Figur. Testen Sie den Algorithmus mit einer Python-Implementierung.

29 Rekursive Verarbeitung von Listen
Teil 3 Rekursive Verarbeitung von Listen

30 Einstieg - Geschachtelte Listen
Eine Gästeliste soll mit einem Programm verwaltet und verarbeitet werden. gaeste = ["Ursula", "Winfried", "Ulrike", "Klaus", ...] def ausgabe(liste): i = 0 while i < len(liste): element = liste[i] print element i = i + 1 def ausgabe(liste): if len(liste) == 0: pass else: erstesElement = liste[0] restListe = liste[1:] print erstesElement ausgabe(restListe) iterativ # Test ausgabe(gaeste) rekursiv

31 Einstieg - Geschachtelte Listen
Eine Gästeliste soll mit einem Programm verwaltet und verarbeitet werden. gaeste = \ [ \ ["Ursula", "Winfried"], \ ["Ulrike", "Klaus"], \ ["Christiane", "Tim"], \ ["Andreas"], \ ["Ulrike", "Peter", ["Kea", "Lena", "Paula"]], \ ... ] def ausgabe(liste): if len(liste) == 0: pass else: erstesElement = liste[0] restListe = liste[1:] if type(erstesElement) == list: ausgabe(erstesElement) print erstesElement ausgabe(restListe) ? iterativ rekursiv # Test ausgabe(gaeste)

32 Einstieg - Geschachtelte Listen
ausgabe([["Ursula", "Winfried"], ["Ulrike", "Klaus"], ["Christiane", "Tim"], ...]) ausgabe(["Ursula", "Winfried"]) print "Ursula" ausgabe(["Winfried"]) print "Winfried" ausgabe([]) pass ausgabe([["Ulrike", "Klaus"], ["Christiane", "Tim"], ...] ausgabe(["Ulrike", "Klaus"]) print "Ulrike" ausgabe(["Klaus"]) print "Klaus" ausgabe([]) ausgabe([["Christiane", "Tim"], ...]) ... def ausgabe(liste): if len(liste) == 0: pass else: erstesElement = liste[0] restListe = liste[1:] if type(erstesElement) == list: ausgabe(erstesElement) print erstesElement ausgabe(restListe) Ausführung rekursiver Algorithmus

33 Liste als rekursive Datenstruktur
Eine Liste ist entweder eine leere Liste, oder besteht aus einem ersten Element und einer (Rest-)Liste. def ausgabe(liste): if len(liste) == 0: # liste == [] pass else: # liste == [erstesElement] + restListe erstesElement = liste[0] restListe = liste[1:] print erstesElement ausgabe(restListe) def ausgabe(liste): if len(liste) == 0: # liste == [] pass else: # liste == [erstesElement] + restListe erstesElement = liste[0] restListe = liste[1:] if type(erstesElement) == list: ausgabe(erstesElement) else: print erstesElement ausgabe(restListe)

34 Entwicklung rekursiver Algorithmen
Problem: Es soll gezählt werden, wie oft ein Element in einer Liste vorkommt. Fall 1: Bearbeite eine leere Liste anzahl('b', []) -> Reduktionsanfang: Löse das Problem direkt Fall 2: Bearbeite eine nicht-leere Liste anzahl('b', ['b', 'b', 'd', 'a', 'c', 'b']) -> 1 + anzahl('b', ['b', 'd', 'a', 'c', 'b']) anzahl('b', ['a', 'b', 'b', 'd', 'a', 'c', 'b']) -> anzahl('b', ['b', 'b', 'd', 'a', 'c', 'b']) Rekursionsschritt: Löse ein entsprechendes Problem Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber „verkleinertes“ Problem.

35 Entwicklung rekursiver Algorithmen
Problem: Es soll gezählt werden, wie oft ein Element in einer Liste vorkommt. anzahl('b', []) -> anzahl('b', ['b', 'b', 'd', 'a', 'c', 'b']) -> 1 + anzahl('b', ['b', 'd', 'a', 'c', 'b']) anzahl('b', ['a', 'b', 'b', 'd', 'a', 'c', 'b']) -> anzahl('b', ['b', 'b', 'd', 'a', 'c', 'b']) def anzahl(element, liste): if len(liste) == 0: return 0 else: if liste[0] == element: return (1 + anzahl(element, liste[1:])) return anzahl(element, liste[1:]) (rekursive) Reduktionsschritte (rekursive) Reduktionsregeln Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber „verkleinertes“ Problem.

36 Entwicklung rekursiver Algorithmen
Problem: Es soll gezählt werden, wie oft ein Element in einer Liste vorkommt. anzahl('b', ['a', 'b', 'd', 'a', 'b']) -> anzahl('b', ['b', 'd', 'a', 'b']) -> 1 + anzahl('b', ['d', 'a', 'b']) -> 1 + anzahl('b', ['a', 'b']) -> 1 + anzahl('b', ['b']) -> 1 + (1 + anzahl('b', [])) -> 1 + (1 + 0) -> 2 def anzahl(element, liste): if len(liste) == 0: return 0 else: if liste[0] == element: return (1 + anzahl(element, liste[1:])) return anzahl(element, liste[1:]) Reduktionskette (rekursive) Reduktionsregeln >>> anzahl('b', ['a', 'b', 'd', 'a', 'b']) 2

37 Übungen (siehe 9.2.3) Rekursionsgymnastik: Bearbeiten Sie die Aufgaben des Abschnitts

38 Fallstudie - geometrische Objekte
In den folgenden Aufgaben (siehe 9.3.4) geht es um die Verwaltung und Verarbeitung geometrischer Objekte. Wir betrachten vereinfachend nur geometrische Objekte, die aus Streckenzügen mit Punkten mit ganzzahligen Koordinaten bestehen. Die folgende Abbildung (Logo des Fachbereichs Informatik der TU Kaiserslautern) ist aus solchen geometrischen Objekte aufgebaut. stuetzelinks = [[0, 0],[20, 0],[50, 100],[30, 100],[0, 0]] blockunten = [[90, 10],[110, 10],[110, 30],[90, 30],[90, 10]] blockoben = [[90, 70],[110, 70],[110, 90],[90, 90],[90, 70]] raute = [[80, 50],[100, 40],[120, 50],[100, 60],[80, 50]] verbindung1 = [[100, 110], [100, 90]] verbindung2 = [[100, 70], [100, 60]] verbindung3 = [[100, 40], [100, 30]] verbindung4 = [[100, 10], [100, 0]] verbindung5 = [[80, 50], [70, 50], [70, 100], [100, 100]] stuetzerechts = [verbindung1, blockoben, verbindung2, raute, \ verbindung5, verbindung3, blockunten, verbindung4] dach = [[10, 110],[130, 110],[130, 125],[70, 140],[10, 125],[10, 110]] tor = [stuetzelinks, stuetzerechts, dach] rahmen = [[0, 0],[140, 0],[140, 140],[0, 140],[0, 0]] logo = [tor, rahmen] Bearbeiten Sie die Aufgaben aus Abschnitt

39 Rekursive Verarbeitung natürlicher Zahlen
Teil 4 Rekursive Verarbeitung natürlicher Zahlen

40 Einstieg - Wege im Galton-Brett
Ein Galton-Brett besteht aus Stäben, die in Reihen untereinander versetzt angeordnet sind. Wenn man eine Kugel ein solches Galton-Brett herunterrollen lässt, dann trifft es auf jeweils auf einen Stab und rollt dann entweder links oder rechts davon weiter herunter.

41 Einstieg - Aufgabe (siehe 9.4.1)
Die Stäbe in der Abbildung oben sind bereits mit Stab-Koordinaten versehen. Mit diesen Koordinaten könnte man einen möglichen Kugelweg so beschreiben: (0,0), (1, 0), (2, 1), (3, 2), (4, 3), (5, 3). Wie viele Wege gibt es im Galton-Brett bis zum Stab (m, n)? Bestimmen Sie für alle Stäbe erst einmal die jeweilige Anzahl. Zur Kontrolle: Bis zum Stab (5, 3) gibt es 10 Wege.

42 Einstieg - Aufgabe (siehe 9.4.1)
Die Funktion galton(m, n) beschreibe die Anzahl der Wege im Galton-Brett bis zum Stab (m, n). Begründen Sie die unten formulierten Eigenschaften der Funktion galton. galton(2, 0) galton(n, 0) -> 1 galton(n, n) -> 1 galton(m, n) -> galton(m-1, n-1) + galton(m-1, n), falls m > 0 und 0 < n < m gilt.

43 Einstieg - Aufgabe (siehe 9.4.1)
Testen Sie die folgenden Funktionsdefinitionen. Worin unterscheiden sie sich? Welche ist korrekt? def galton(m, n): if m < n: return None else: if (n == 0) or (m == n): return 1 return (galton(m-1, n-1) + galton(m-1, n)) def galton(m, n): if m < n: return 0 else: if n == 0: return 1 return (galton(m-1, n-1) + galton(m-1, n)) def galton(m, n): if m == 0: if n == 0: return 1 else: return 0 return (galton(m-1, n-1) + galton(m-1, n))

44 Rekursive Struktur natürlicher Zahlen
Eine natürliche Zahl ist entweder eine Null oder Nachfolger einer natürlichen Zahl. Problem: Die Summe der ersten n natürlichen Zahlen soll berechnet werden. Fall 1: Bearbeite die Zahl 0. summe(0) -> Reduktionsanfang: Löse das Problem direkt Fall 2: Bearbeite den Nachfolger einer natürlichen Zahl summe(5) -> 5 + summe(4) Rekursionsschritt: Löse ein entsprechendes Problem Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber „verkleinertes“ Problem.

45 Entwicklung rekursiver Algorithmen
Problem: Die Summe der ersten n natürlichen Zahlen soll berechnet werden. summe(0) -> summe(5) -> 5 + summe(4) def summe(zahl): if zahl == 0: return 0 else: return zahl + summe(zahl-1) (rekursive) Reduktionsschritte (rekursive) Reduktionsregeln Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber „verkleinertes“ Problem.

46 Entwicklung rekursiver Algorithmen
Problem: Die Summe der ersten n natürlichen Zahlen soll berechnet werden. summe(5) -> 5 + summe(4) -> 5 + (4 + summe(3)) -> 5 + (4 + (3 + summe(2))) -> 5 + (4 + (3 + (2 + summe(1)))) -> 5 + (4 + (3 + (2 + (1 + summe(0))))) -> 5 + (4 + (3 + (2 + (1 + 0)))) -> 5 + (4 + (3 + (2 + 1))) -> 5 + (4 + (3 + 3)) -> 5 + (4 + 6) -> > 15 def summe(zahl): if zahl == 0: return 0 else: return zahl + summe(zahl-1) (rekursive) Reduktionsregeln >>> summe(5) 15 Reduktionskette

47 Übungen (siehe 9.4.3) Rekursionsgymnastik: Bearbeiten Sie die Aufgaben des Abschnitts

48 Fallstudie - natürliche Zahlen
Operationen auf natürlichen Zahlen lassen sich alle aus einer einzigen Grundoperationen entwickeln. Man benötigt hierzu nur die Nachfolger-Operation s: N -> N, die jeder natürlichen Zahl n ihren Nachfolger s(n) zuordnet. add(x,0) -> x add(x,s(y)) -> s(add(x,y)) def s(x): return x+1 def p(x): return x-1 def add(x, y): if y == 0: return x else: return s(add(x, p(y))) (rekursive) Reduktionsregeln Implementierung in Python Bearbeiten Sie die Aufgaben aus Abschnitt

49 Rekursion und Berechnungsaufwand
Teil 5 Rekursion und Berechnungsaufwand

50 Einstieg - Wege im Galton-Brett
Warum handelt es sich hier um ein ineffizientes Berechnungsverfahren? >>> galton(5, 3) galton( 5 , 3 ) galton( 4 , 2 ) galton( 3 , 1 ) galton( 2 , 0 ) galton( 2 , 1 ) galton( 1 , 0 ) galton( 1 , 1 ) galton( 3 , 2 ) galton( 2 , 2 ) galton( 4 , 3 ) galton( 3 , 3 ) 10 def galton(m, n): print "galton(", m, ",", n, ")" if m < n: return None else: if (n == 0) or (m == n): return 1 return (galton(m-1, n-1) + galton(m-1, n))

51 Ackermann-Funktion "Die Ackermannfunktion ist eine 1926 von Wilhelm Ackermann gefundene, extrem schnell wachsende mathematische Funktion, mit deren Hilfe in der theoretischen Informatik Grenzen von Computer- und Berechnungsmodellen aufgezeigt werden können. Heute gibt es eine ganze Reihe von Funktionen, die als Ackermannfunktion bezeichnet werden. Diese weisen alle ein ähnliches Bildungsgesetz wie die ursprüngliche Ackermannfunktion auf und haben auch ein ähnliches Wachstumsverhalten." (wikipedia) ack(0, y) -> y+1 ack(x, 0) -> ack(x-1, 1), falls x > 0 ack(x, y) -> ack(x-1, ack(x, y-1)), falls y > 0

52 Auswertung der Ackermann-Funktion
Setzen Sie die Reduktionskette einige Schritte weiter fort. Wollen Sie es zu Ende rechnen? ack(0, y) -> y+1 ack(x, 0) -> ack(x-1, 1), falls x > 0 ack(x, y) -> ack(x-1, ack(x, y-1)), falls y > 0 ack(3, 2) -> ack(2, ack(3, 1)) -> ack(2, ack(2, ack(3, 0))) -> ack(2, ack(2, ack(2, 1))) ->

53 Implementierung d. Ackermann-Funkt.
Implementieren Sie die Ackermann-Funktion in Python so, dass jeder Funktionsaufruf mit den aktuellen Parameterwerten auf dem Bildschirm ausgegeben wird (vgl. galton). Testen Sie verschiedene Funktionsaufrufe wie z. B. ack(2, 3) und ack(3, 2). Was fällt auf? ack(0, y) -> y+1 ack(x, 0) -> ack(x-1, 1), falls x > 0 ack(x, y) -> ack(x-1, ack(x, y-1)), falls y > 0

54 Eigenschaften der Ackermann-Funktion
Informieren Sie sich (z. B. bei Wikipedia) über das Wachstumsverhalten der Ackermann-Funktion. Quelle:

55 Berechnungsaufwand Warum stößt man bei der Berechnung der Ackermann-Funktion sehr schnell auf Grenzen - sowohl hinsichtlich der Rechenzeit als auch hinsichtlich des Speicherbedarfs? ack(4, 3) -> ack(3, ack(4, 2)) -> ack(3, ack(3, ack(4, 1))) -> ack(3, ack(3, ack(3, ack(4, 0)))) -> ack(3, ack(3, ack(3, ack(3, 1)))) -> ack(3, ack(3, ack(3, ack(2, ack(3, 0))))) -> ack(3, ack(3, ack(3, ack(2, ack(2, 1))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, ack(2, 0)))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, ack(1, 1)))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, ack(1, 0))))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, ack(0, 1))))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, 2)))))) -> ack(3, ack(3, ack(3, ack(2, ack(1, 3))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(1, 2)))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(1, 1))))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, ack(1, 0)))))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, ack(0, 1)))))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, 2)))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, 3))))) -> ack(3, ack(3, ack(3, ack(2, ack(0, 4))))) -> ack(3, ack(3, ack(3, ack(2, 5)))) -> ... ack(3, ack(3, ack(3, 13))) -> ... ack(3, ack(3, 65533)) -> ...

56 Teil 6 Rekursion und Iteration - Umwandlung rekursiver Algorithmen in iterative Algorithmen

57 Äquivalente Algorithmen
Zur rekursiv definierten Fakultätsfunktion lässt sich leicht ein äquivalenter iterativer Berechnungsalgorithmus angeben: def fak(n): if n == 0: return 1 else: return n * fak(n-1) def fak(n): i = n erg = 1 while not i == 0: erg = erg * i i = i - 1 return erg Rekursion Iteration

58 Umwandlungsverfahren
Ein allgemeines Umwandlungsverfahren erhält man, indem man die Reduktionsschritte bei der Auswertung von Termen simuliert. Dabei benutzt man zwei Stapel, um den aktuellen Berechnungszustand darzustellen. def ack(m, n): if m == 0: return n+1 else: if n == 0: return ack(m-1, 1) return ack(m-1, ack(m, n-1)) ack(2, 3) -> ack(1, ack(2, 2)) -> ack(1, ack(1, ack(2, 0))) -> ack(1, ack(1, ack(1, 1))) -> ack(1, ack(1, ack(0, ack(1, 0)))) -> ack(1, ack(1, ack(0, ack(0, 1)))) -> ack(1, ack(1, ack(0, 2))) -> ... Auswertung durch Reduktionsschritte Simulation mit Hilfe von Stapeln

59 Umwandlungsverfahren
t s while t.size() > 0: e = t.top() t.pop() if type(e) == int: s.push(e) else: m = s.top() s.pop() n = s.top() if m == 0: t.push(n+1) if n == 0: t.push("a") t.push(m-1) t.push(1) t.push(m) t.push(n-1) def ack(m, n): if m == 0: return n+1 else: if n == 0: return ack(m-1, 1) return ack(m-1, ack(m, n-1))

60 Umwandlungsverfahren
t s while t.size() > 0: e = t.top() t.pop() if type(e) == int: s.push(e) else: m = s.top() s.pop() n = s.top() if m == 0: t.push(n+1) if n == 0: t.push("a") t.push(m-1) t.push(1) t.push(m) t.push(n-1) def auswerten(term): t = Stapel() t.setStapel(term) s = Stapel() while t.size() > 0: # siehe links return s.top() def ack(m, n): term = [n, m, "a"] return auswerten(term) äquivalenter iterativerAlgorithmus def ack(m, n): if m == 0: return n+1 else: if n == 0: return ack(m-1, 1) return ack(m-1, ack(m, n-1)) rekursiver Algorithmus

61 Umwandlungsverfahren
t s Beachte: Die iterative Ausführung rekursiver Algorithmen ist deshalb von besonderer Bedeutung, weil rekursive Algorithmen (derzeit) immer auf sequentiell arbeitenden Maschinen ausgeführt werden. Das Umwandlungsverfahren zeigt exemplarisch, dass eine solche iterative Ausführung immer möglich ist.

62 Übungen (siehe 9.6.1) Testen Sie die Implementierung des Umwandlungsverfahrens (siehe 9.6.1). Übertragen Sie das Umwandlungsverfahren auch auf die Funktion galton (siehe Aufgabe 2).

63 Teil 7 Rekursion und Iteration - Umwandlung iterativer Algorithmen in rekursive Algorithmen

64 Äquivalente Algorithmen
Zur iterativ definierten Potenzfunktion lässt sich leicht ein äquivalenter rekursiver Berechnungsalgorithmus angeben: def pot(a, n): p = 1 while n > 0: p = p * a n = n - 1 return p def pot(a, n): if n == 0: return 1 else: return a * pot(a, n-1) Iteration Rekursion

65 Umwandlungsverfahren
Ein allgemeines Umwandlungsverfahren erhält man, indem man die Auswertungsschritte bei der Abarbeitung des Algorithmus simuliert. def pot(a, n): p = 1 while n > 0: p = p * a n = n - 1 return p {a -> 2; n -> 3} p = 1 while n > 0: p = p * a n = n - 1 {a -> 2; n -> 3; p -> 1} {a -> 2; n -> 2; p -> 2} ... {a -> 2; n -> 0; p -> 8} Abarbeitung des Algorithmus

66 Umwandlungsverfahren
Das folgende Ablaufprotokoll zeigt, wie die Daten mit Hilfe von Listen, Tupeln etc. dargestellt werden sollen. [('a', 2), ('n', 3)] [('=', 'p', 1), ('while', ('>', 'n', 0), [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1))])] [('a', 2), ('n', 3), ('p', 1)] [('while', ('>', 'n', 0), [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1))])] [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1)), ('while', ('>', 'n', 0), [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1))])] [('a', 2), ('n', 3), ('p', 2)] [('=', 'n', ('-', 'n', 1)), ('while', ('>', 'n', 0), [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1))])] ... [('a', 2), ('n', 0), ('p', 8)] [] {a -> 2; n -> 3} p = 1 while n > 0: p = p * a n = n - 1 {a -> 2; n -> 3; p -> 1} {a -> 2; n -> 3; p -> 2} ... {a -> 2; n -> 0; p -> 8}

67 Umwandlungsverfahren
Zur Erzeugung des Ablaufprotokolls werden folgende Hilfsfunktionen benutzt: >>> VariablenWert('y', [('x', 4), ('y', 3), ('z', 7)]) 3 >>> VariablenWert('a', [('x', 4), ('y', 3), ('z', 7)]) '?' >>> NeuerZustand('y', 6, [('x', 4), ('y', 3), ('z', 7)]) [('x', 4), ('y', 6), ('z', 7)] >>> NeuerZustand('a', 0, [('x', 4), ('y', 3), ('z', 7)]) [('x', 4), ('y', 3), ('z', 7), ('a', 0)] >>> TermWert(('+', 'z', 4), [('x', 4), ('y', 3), ('z', 7)]) 11 >>> TermWert(('+', 'z', ('+', 'x', 'x')), [('x', 4), ('y', 3), ('z', 7)]) 15 >>> BooleWert(('>', 'x', 4), [('x', 4), ('y', 3), ('z', 7)]) False >>> BooleWert(('==', 'x', 4), [('x', 4), ('y', 3), ('z', 7)]) True

68 Umwandlungsverfahren
Zur Erzeugung des Ablaufprotokolls werden folgende Hilfsfunktionen benutzt: >>> AnweisungenAusfuehren([('=', 'x', 2), ('if', ('>', 'x', 3), [('=', 'y', '0')], [('=', 'y', 1)])], []) [('x', 2), ('y', 1)] >>> AnweisungenAusfuehren([('while', ('>', 'u', 0), [('=', 'u', ('-', 'u', 1))])], [('u', 3)]) [('u', 0)] >>> AnweisungenAusfuehren([('=', 'p', 1), ('while', ('>', 'n', 0), [('=', 'p', ('*', 'p', 'a')), ('=', 'n', ('-', 'n', 1))])], [('a', 2), ('n', 3)]) [('a', 2), ('n', 0), ('p', 8)]

69 Umwandlungsverfahren
def pot(a, n): p = 1 while n > 0: p = p * a n = n - 1 return p iterativer Algorithmus def potenz(a, n): return VariablenWert('p', \ AnweisungenAusfuehren(\ [\ ('=', 'p', 1), \ ('while', ('>', 'n', 0), \ ('=', 'p', ('*', 'p', 'a')), \ ('=', 'n', ('-', 'n', 1))\ ])\ ], \ [('a', a), ('n', n)])) äquivalenter rekursiver Algorithmus

70 Übungen (siehe 9.6.2) Testen Sie die Implementierung des Umwandlungsverfahrens (siehe 9.6.2). Übertragen Sie das Umwandlungsverfahren auch auf eine andere Definition der Potenzfunktion (siehe Aufgabe 1/3).


Herunterladen ppt "Problemlösen mit Rekursion"

Ähnliche Präsentationen


Google-Anzeigen