Powerpoint-Präsentation

Werbung
Slide 1

Problemlösen mit Rekursion

Klaus Becker
2009


Slide 2

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


Slide 3

3

Teil 1

Problemlösen durch Problemreduktion


Slide 4

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.


Slide 5

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.: http://www.mpg-trier.de/d7/prog/hanoi/hanoi.htm


Slide 6

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


Slide 7

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


Slide 8

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


Slide 9

Algorithmus

9

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:
transportiere eine Scheibe von X nach Z


Slide 10

Rekursive Problemreduktion

10

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:
transportiere eine Scheibe von X nach Z
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.


Slide 11

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:
transportiere eine Scheibe von X nach Z
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

Ausführungstiefe: 1


Slide 12

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:
transportiere eine Scheibe von X nach Z
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
transportiere einen 1-Scheiben-Turm von A über B nach C

Ausführungstiefe: 2


Slide 13

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:
transportiere eine Scheibe von X nach Z
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 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 A
transportiere eine Scheibe von B nach C
transportiere einen 1-Scheiben-Turm von A über B nach C:
transportiere eine Scheibe von A nach C

Ausführungstiefe: 3


Slide 14

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 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 A
transportiere eine Scheibe von B nach C
transportiere einen 1-Scheiben-Turm von A über B nach C:
transportiere eine Scheibe von A nach C

Basisaktionen


Slide 15

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:
transportiere eine Scheibe von X nach Z
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:
print "transportiere eine Scheibe von ", x, " nach ", z

Algorithmus

Python-Programm


Slide 16

16

Übungen (siehe 9.1.4)

Bearbeiten Sie die Aufgaben 1, 2.


Slide 17

17

Teil 2

Selbstähnliche Figuren


Slide 18

18

Einstieg - Selbstähnliche Figur

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


Slide 19

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)
zeichne_Baum(100)
drehe_dich_nach_rechts(45)
gehe_rueckwaerts(200)

rekursive
Problemreduktion

ALG zeichne_Baum(x):
wenn x >-> 2:
gehe_vorwaerts(x)
drehe_dich_nach_rechts(45)
zeichne_Baum(x/2)
drehe_dich_nach_links(90)
zeichne_Baum(x/2)
drehe_dich_nach_rechts(45)
gehe_rueckwaerts(x)
rekursiver
Algorithmus


Slide 20

Exkurs - Turtle-Grafik

20

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)
Turtle-Algorithmus

 stift_hoch
 stift_runter
 gehe_vorwaerts(betrag)
 gehe_rueckwaerts(betrag)
 drehe_dich_nach_links(winkel)
 drehe_dich_nach_rechts(winkel)
 gehe_zu_punkt(punkt)
...


Slide 21

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-8859-1 -*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)


Slide 22

22

Exkurs - Turtle-Grafik in Python

# -*- coding: iso-8859-1 -*from turtle import *
# Deklaration einer
Zeichenprozedur
def baum(stamm):
if stamm >= 2:
t.forward(stamm)
t.right(45)
baum(stamm/2)
t.left(90)
baum(stamm/2)
t.right(45)
t.backward(stamm)
# Erzeugung eines Turtle-Objekts
t = Turtle()
# Test der Zeichenprozedur
t.left(90)
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)
zeichne_Baum(x/2)
drehe_dich_nach_rechts(45)
gehe_rueckwaerts(x)


Slide 23

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.


Slide 24

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.


Slide 25

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.


Slide 26

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.


Slide 27

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.


Slide 28

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.


Slide 29

29

Teil 3

Rekursive Verarbeitung von Listen


Slide 30

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
iterativ

# Test
ausgabe(gaeste)

def ausgabe(liste):
if len(liste) == 0:
pass
else:
erstesElement = liste[0]
restListe = liste[1:]
print erstesElement
ausgabe(restListe)
rekursiv


Slide 31

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)
else:
print erstesElement
ausgabe(restListe)
iterativ
# Test
ausgabe(gaeste)

rekursiv


Slide 32

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"
def ausgabe(liste):
ausgabe([])
if len(liste) == 0:
pass
pass
ausgabe([["Christiane", "Tim"], ...])
else:
...
erstesElement = liste[0]
restListe = liste[1:]
Ausführung
if type(erstesElement) == list:
ausgabe(erstesElement)
else:
print erstesElement
rekursiver Algorithmus
ausgabe(restListe)


Slide 33

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)


Slide 34

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', []) ->
0

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'])

Rekursionsschritt:
Löse ein entsprechendes Problem

anzahl('b', ['a', 'b', 'b', 'd', 'a', 'c', 'b']) ->
anzahl('b', ['b', 'b', 'd', 'a', 'c', 'b'])

Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber
„verkleinertes“ Problem.


Slide 35

35

Entwicklung rekursiver Algorithmen

Problem: Es soll gezählt werden, wie oft ein Element in einer Liste vorkommt.

anzahl('b', []) ->
0
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'])
(rekursive) Reduktionsschritte

def anzahl(element, liste):
if len(liste) == 0:
return 0
else:
if liste[0] == element:
return (1 + anzahl(element, liste[1:]))
else:
return anzahl(element, liste[1:])
(rekursive) Reduktionsregeln

Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber
„verkleinertes“ Problem.


Slide 36

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
Reduktionskette

def anzahl(element, liste):
if len(liste) == 0:
return 0
else:
if liste[0] == element:
return (1 + anzahl(element, liste[1:]))
else:
return anzahl(element, liste[1:])
(rekursive) Reduktionsregeln

>>> anzahl('b', ['a', 'b', 'd', 'a', 'b'])
2


Slide 37

37

Übungen (siehe 9.2.3)

Rekursionsgymnastik: Bearbeiten Sie die Aufgaben des Abschnitts 9.2.3.


Slide 38

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 9.3.4.


Slide 39

39

Teil 4

Rekursive Verarbeitung natürlicher Zahlen


Slide 40

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.


Slide 41

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.


Slide 42

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.


Slide 43

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:
def galton(m, n):
return None
if m < n:
else:
return 0
if (n == 0) or (m == n):
else:
return 1
if n == 0:
else:
return 1
return (galton(m-1, n-1) + galton(m-1, n))
else:
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
else:
if n == 0:
return 1
else:
return (galton(m-1, n-1) + galton(m-1, n))


Slide 44

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) ->
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.


Slide 45

45

Entwicklung rekursiver Algorithmen

Problem: Die Summe der ersten n natürlichen Zahlen soll berechnet werden.

summe(0) ->
0
summe(5) ->
5 + summe(4)

(rekursive) Reduktionsschritte

def summe(zahl):
if zahl == 0:
return 0
else:
return zahl + summe(zahl-1)

(rekursive) Reduktionsregeln

Rekursive Problemreduktion: Reduziere des Problems auf ein entsprechendes, aber
„verkleinertes“ Problem.


Slide 46

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) ->
5 + 10 ->
15
Reduktionskette

def summe(zahl):
if zahl == 0:
return 0
else:
return zahl + summe(zahl-1)
(rekursive) Reduktionsregeln

>>> summe(5)
15


Slide 47

47

Übungen (siehe 9.4.3)

Rekursionsgymnastik: Bearbeiten Sie die Aufgaben des Abschnitts 9.4.3.


Slide 48

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))
(rekursive) Reduktionsregeln

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)))
Implementierung in Python

Bearbeiten Sie die Aufgaben aus Abschnitt 9.4.4.


Slide 49

49

Teil 5

Rekursion und Berechnungsaufwand


Slide 50

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 , 1 )
galton( 1 , 0 )
galton( 1 , 1 )
galton( 2 , 2 )
galton( 4 , 3 )
galton( 3 , 2 )
galton( 2 , 1 )
galton( 1 , 0 )
galton( 1 , 1 )
galton( 2 , 2 )
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
else:
return (galton(m-1, n-1) + galton(m-1, n))


Slide 51

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


Slide 52

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,
ack(2,
ack(2,
ack(2,

2) ->
ack(3, 1)) ->
ack(2, ack(3, 0))) ->
ack(2, ack(2, 1))) ->


Slide 53

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


Slide 54

54

Eigenschaften der Ackermann-Funktion

Informieren Sie sich (z. B. bei Wikipedia) über das Wachstumsverhalten der AckermannFunktion.

Quelle: http://de.wikipedia.org/wiki/Ackermannfunktion


Slide 55

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)) ->
...


Slide 56

56

Teil 6

Rekursion und Iteration Umwandlung rekursiver Algorithmen in
iterative Algorithmen


Slide 57

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)

Rekursion

def fak(n):
i=n
erg = 1
while not i == 0:
erg = erg * i
i=i-1
return erg
Iteration


Slide 58

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)
else:
return ack(m-1, ack(m, n-1))

ack(2,
ack(1,
ack(1,
ack(1,
ack(1,
ack(1,
ack(1,
...

3) ->
ack(2,
ack(1,
ack(1,
ack(1,
ack(1,
ack(1,

2)) ->
ack(2,
ack(1,
ack(0,
ack(0,
ack(0,

0))) ->
1))) ->
ack(1, 0)))) ->
ack(0, 1)))) ->
2))) ->

Auswertung durch
Reduktionsschritte

Simulation mit Hilfe von
Stapeln


Slide 59

Umwandlungsverfahren

59
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()
s.pop()
if m == 0:
t.push(n+1)
else:
if n == 0:
t.push("a")
t.push(m-1)
t.push(1)
else:
t.push("a")
t.push(m-1)
t.push("a")
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)
else:
return ack(m-1, ack(m, n-1))


Slide 60

Umwandlungsverfahren

60
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()
s.pop()
if m == 0:
t.push(n+1)
else:
if n == 0:
t.push("a")
t.push(m-1)
t.push(1)
else:
t.push("a")
t.push(m-1)
t.push("a")
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):
rekursiver
if m == 0:
Algorithmus
return n+1
else:
if n == 0:
return ack(m-1, 1)
else:
return ack(m-1, ack(m, n-1))


Slide 61

Umwandlungsverfahren

61
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.


Slide 62

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).


Slide 63

63

Teil 7

Rekursion und Iteration Umwandlung iterativer Algorithmen in
rekursive Algorithmen


Slide 64

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


Slide 65

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

Abarbeitung des
Algorithmus

{a -> 2; n ->
p=1
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
p=p*a
n=n-1
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
...
{a -> 2; n ->

3}

3; p -> 1}

3; p -> 1}

2; p -> 2}
0; p -> 8}


Slide 66

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))])]
[('a', 2), ('n', 3), ('p', 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 ->
p=1
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
p=p*a
n=n-1
while n > 0:
p=p*a
n=n-1
{a -> 2; n ->
...
{a -> 2; n ->

3}

3; p -> 1}

3; p -> 1}

3; p -> 2}
0; p -> 8}


Slide 67

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


Slide 68

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)]


Slide 69

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


Slide 70

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