Upload
trinhcong
View
217
Download
0
Embed Size (px)
Citation preview
László Böszörményi ESOP Rekursive Algorithmen - 1
• Die Aufrufkette von Methoden bildet einen Kreis– Direkte Rekursion
• Methode ruft sich selber an: P → P
– Indirekte Rekursion• Methode wird indirekt „zurückgerufen“: P → Q, Q → R, R → P• „Länge“ des Kreises > 1
• Bei jedem Aufruf wird eine neue Instanz von Parametern und lokalen Variablen angelegt (gestapelt)
• Ähnliche Regeln wie bei rekursiven Definitionen1. Eine rekursive Prozedur muss einen nicht rekursiven Teil
haben (Trivialfall)2. Die Rekursion muss enden – zum Trivialfall führen
László Böszörményi ESOP Rekursive Algorithmen - 2
Fakultätsfunktion• Nicht-rekursive Definition (n! = 1 * 2 * . . . n)
– 0! = 1, n! = Πni = 1i, für n ≥ 1
• Nicht-rekursive Implementierungint Faculty (int n) { // Berechnet die Fakultätsfunktion für n ≥ 0
int f = 1; // f wird auf 1 initialisiertfor (int i = 1; i <= n; i++) f = f * i; // f = 1 * 2 * 3 * . . . * nreturn f;
} // Faculty• Rekursive Definition
– 0! = 1, n! = n (n –1)! für n ≥ 1• Rekursive Implementierung
int Faculty (int n) { // Berechnet die Fakultätsfunktion für n ≥ 0if (n == 0 ) return 1; // Trivialfallelse return n * Faculty (n - 1); // Rekursiver Aufruf:
} // Faculty // Führt zum Trivialfall (n = 0) 7206120524463221110fn
László Böszörményi ESOP Rekursive Algorithmen - 3
Ablauf der Fakultätsfunktionpublic class Faculty { // 18.09.2000. LB
static int GetPosInt() { . . . } // GetPosInt;
static int Faculty (int n) {if (n == 0 ) return 1;else return n * Faculty (n - 1);
} // Faculty
static void main(String [] args) {int N;Out.println("Fakultaetsfunktion");N = GetPosInt (); // Eingabe: 6Out.println("Fakultaet von " + N + ": "
+ Faculty(N));} // main
} // FacultyFakultaet von 6: 720
main 720
Fac(6) 6 * 120
Fac(5) 5 * 24
Fac(4) 4 * 6
Fac(3) 3 * 2
Fac(2) 2 * 1
Fac(1) 1 * 1
Fac(0) 1
László Böszörményi ESOP Rekursive Algorithmen - 4
Zahlenreihe umdrehenpublic class Reverse {
static int GetInt() { . . .} // GetInt
static void Reverse () {int i; // Bei jedem Aufruf von Reverse, neue Instanz von ii = GetInt(); // Liest nächste Zahl von der Tastaturif (i != 0) { // Bei Eingabewert == 0: Ende
Reverse(); // Nächster Aufruf: Stack wächstOut.println(i);
} // if} // Reverse
static void main(String [] args) {Out.println("Zahlenreihe umdrehen, endet mit 0");Reverse();
} // main} // Reverse
Eingabe: 2 4 6 8 10 0
Ausgabe: 10 8 6 4 2
i1 == 2i2 == 4i3 == 6i4 == 8Top of Stack nach
dem 4. Aufruf
Zu dem Punkt kommt die Ausführung erst beim „Aufstieg“ aus dem Trivialfall
László Böszörményi ESOP Rekursive Algorithmen - 5
Fibonacci Zahlen• Fib(0) = 1, Fib(1) = 1, Fib(n) = Fib(n-1) + Fib(n-2)• Implementierung:
static int Fib (int n) {if ( n <= 1) return 1; // n == 0 ∨ n == 1else return Fib(n - 1) + Fib(n - 2);
} // Fib• Ineffizient: Fib(5)
Fib(4)
Fib(3) Fib(2)
Fib(2) Fib(1)
Fib(1) Fib(0)
Fib(1) Fib(0)
Fib(3)
Fib(2) Fib(1)
Fib(1) Fib(0)2
3 2
5
8
2
3
217136855433221110Fn
László Böszörményi ESOP Rekursive Algorithmen - 6
• Rekursive Probleme1. Der Problembereich lässt sich in immer kleinere Teilprobleme
aufspalten, bis sie trivial werden (divide et impera)2. Bearbeitung rekursiver Datenstrukturen (siehe Kapitel 9)
• Finden von rekursiven Lösungen1. Suche den rekursiven Fall (der sich teilen lässt)2. Definiere die Endbedingung3. Prüfe Konvergenz
Gegenbeispiel:static int Wrong (int n) { // Fehlerhafte rekursive Prozedur
if (n == 1) return 1; // Falsche Endbedingung!else return n + Wrong(n - 2); // n + (n-2) + (n-4) + (n-6) + . . .
} // WrongHält nicht beim Aufruf mit geraden Zahlen
László Böszörményi ESOP Rekursive Algorithmen - 7
QuickSort (C.A.R. Hoare)1. Nehme aus dem Array ein beliebiges Element x2. Partitioniere es so, dass alle Elemente in der linken Hälfte ≤ x,
die in der Rechten ≥ x sind (vertausche die, die falsch stehen)3. Wende obigen Algorithmus rekursiv auf die linke bzw. rechte
Hälfte an, bis das Array trivial wird (enthält nur 1 Element)static void QuickSort (int [] a, int left, int right) { //Sortiert a
int i= left, j = right, x = a[(left + right) / 2], w; // x: mittleres Elementdo { while (a[i] < x) i++; // Suche Elemente,
while (a[j] > x) j--; // die zum tauschen sindif (i <= j) { // i > j: nichts zu tun
w = a[i]; a[i] = a[j]; a[j] = w; // Tauschi++; j--;
} // if i <= j} while (i <= j); // i > j: Ende der Partitionierungif (left < j) QuickSort (a, left, j);if (i < right) QuickSort (a, i, right);
} // QuickSort
Partitio-nierung
Rekur-sion
16 5 3 8 9 1 25 44 5 3 1 9 8 25 16 4 1 3 5 9 8 25 16 1 4 3 5 9 8 25 16 1 3 4 5 9 8 25 161 3 4 5 8 9 25 161 3 4 5 8 9 16 25 1 3 4 5 8 9 16 25
a0 a1 a2 a3 a4 a5 a6 a7
László Böszörményi ESOP Rekursive Algorithmen - 8
Die Türme von Hanoi• Drei Türme (Pfosten): Start, Finish und Temp• Auf Start liegen H Scheiben, der Größe nach sortiert, die
Größte ist unten• Wir müssen die Scheiben auf Finish transferieren so, dass
sie dort genauso geordnet sind• Wir dürfen immer nur eine Scheibe bewegen, und eine
größere darf nie auf einer kleineren liegen• Als Hilfe können wir den Pfosten Temp benutzen • Rekursive Lösung
– H = 0: nichts zu machen: Trivialfall– H > 0, wende die folgenden Regeln rekursiv an:
1. Turm der Höhe H – 1 von Start nach Temp bewegen (mittels Finish)2. Unterste Scheibe (eine Turm, mit Höhe = 1) von Start nach Finish
legen – die liegt nun richtig3. Turm der Höhe H – 1 von Temp nach Finish bewegen (mittels Start)
László Böszörményi ESOP Rekursive Algorithmen - 9
Türme von Hanoi – Datenstrukturenpublic class Hanoi { // Die Tuerme von Hanoi, 21.10.2000. LB
static final int Start = 0, Finish = 1, Temp= 2; // 3 Pfostenint H; // Höhe der Pfostenclass State{ // Zustand eines Pfostens
int top = 0; // Oberste Scheibenpositionint [] disks; // Scheiben-Nummer: 1 .. H, 0 für keine ScheibeState() {
disks = new int [H + 1]; // Scheiben: 1 .. H (statt 0 .. H-1 – bequemer)for (int j = 1; j <= H; j++) disks[j] = 0; // disks[0] unbenutzt
} // State} // StateState[] posts= new State [3]; // 3 Pfosten: Start, Finish, TempHanoi (int Hoehe) {
H = Hoehe;for (int i = 0; i < posts.length; i++) posts[i] = new State();posts[Start].top = H; // Start-Pfosten am Anfang: H Scheibenfor (int i = 1; i <= H; i++) posts[Start].disks[i] = H - (i - 1); // Position-1: Scheibe-H
} // Hanoi Position-H: Scheibe-1
László Böszörményi ESOP Rekursive Algorithmen - 10
Türme von Hanoi – Graphische Darstellungvoid line (int num, char muster) { // Schreibt muster num-mal aus
while (num > 0) { Out.print(muster); num--; } } // line void disk (int d) { // Darstellung einer Scheibe
if (d == 0) { // Die "leere" Scheibe"line (H, ' '); line (1, '|'); line (H, ' '); }
else { // Darstellung der Scheibe-dline (H - d, ' '); line (3 + 2*(d - 1), '='); line (H - d, ' '); } // if d
} // diskvoid display () {
for (int p = 0; p < posts.length; p++) disk(0); // Oben nur „nackte“ PfostenOut.println();for (int line = H; line > 0; line--) { // Von Oben gezeichnet
for (int p = 0; p < posts.length; p++) disk(posts[p].disks[line]);Out.println();
} // for lineOut.println();
} // display
László Böszörményi ESOP Rekursive Algorithmen - 11
Türme von Hanoi – Rekursive Lösungvoid transfer (int from, int to) { // Bewege eine Scheibe vom Pfosten to nach from
posts[to].top++; // Nächste leere Position am Pfosten toposts[to].disks[posts[to].top] = posts[from].disks[posts[from].top]; // from → toposts[from].disks[posts[from].top] = 0; // Lösche ursprüngliche Scheibeposts[from].top--; // Oberste Position am Pfosten from sinkt
} // transfervoid doPlay (int height, int from, int to, int temp) { // von from nach to mittels temp
if (height > 0) {doPlay(height - 1, from, temp, to); // von from nach temp mittels totransfer(from, to); // Bewege Scheibe from → todoPlay(height - 1, temp, to, from); // von temp nach to mittels from
} // if height > 0} // doPlaystatic void main (String [] args) {
int H = 4; Hanoi tower = new Hanoi(H); tower.display();tower.doPlay(H, Start, Finish, Temp); tower.display();
} // main} // Hanoi
László Böszörményi ESOP Rekursive Algorithmen - 12
Ablauf von Türme von Hanoi – (1)| | |
=== | |===== | |
======= | |
3, Start , Finish, Temp2, Start , Temp , Finish1, Start , Finish, Temp0, Start , Temp , Finish1, Start , Finish, Temp
| | || | |
===== | |======= === |
0, Temp , Finish, Start2, Start , Temp , Finish
| | || | || | |
======= === =====
1, Finish, Temp , Start0, Finish, Start , Temp1, Finish, Temp , Start
| | || | || | ===
======= | =====
0, Start , Temp , Finish3, Start , Finish, Temp
| | || | || | ===| ======= =====
2, Temp , Finish, Start1, Temp , Start , Finish0, Temp , Finish, Start1, Temp , Start , Finish
| | || | || | |
=== ======= =====
3 Scheiben
László Böszörményi ESOP Rekursive Algorithmen - 13
Ablauf von Türme von Hanoi – (2)0, Finish, Start , Temp2, Temp , Finish, Start
| | || | || ===== |
=== ======= |
1, Start , Finish, Temp0, Start , Temp , Finish1, Start , Finish, Temp
| | || === || ===== || ======= |
0, Temp , Finish, Start| | || === || ===== || ======= |
| | |=== | |
===== | |======= | |
========= | |4, Start , Finish, Temp3, Start , Temp , Finish2, Start , Finish, Temp1, Start , Temp , Finish0, Start , Finish, Temp1, Start , Temp , Finish
| | || | |
===== | |======= | |
========= | ===. . .
| | || === || ===== || ======= || ========= |
Mit 4 Scheiben
László Böszörményi ESOP Rekursive Algorithmen - 14
Hilbert Kurven• Eine Hilbert Kurve besteht aus den Teilkurven A, B, C, D• Die Kurven A0, B0, C0, D0 sind leer• Die Kurven Ai, Bi, Ci, Di sind definiert wie folgt
Ai
Di-1Ai-1
Ai-1 Bi-1
Bi
Bi-1Bi-1
Ci-1 Ai-1
Ci
Ci-1Di-1
Bi-1 Ci-1
Di
Ai-1Ci-1
Di-1 Di-1
László Böszörményi ESOP Rekursive Algorithmen - 17
Benutzung von Turtle-Graphics• Ein Stift (ein „Turtle“) bewegt sich auf dem Schirm• Steuerung durch ganz einfache Operationen
– home: Anfangsposition (Mitte des Zeichen-Bereiches)– setPen(x, y): Stift auf Punkt (x, y)– forward(x), backward(x): Bewegung um x Punkte
• forward(x) ≡ backward(-x)
– turnLeft(y), turnRight(y): Richtungsänderung um y Grad• turnLeft(y) ≡ turnRight(360 - y);
– turnTo(y): Richtung in Grad y• EAST = 0, SOUTH = 90, WEST = 180, NORTH = 270
– penUp, penDown: Stift heben bzw. senken– hideTurtle: Der Turtle selbst wird nicht gezeigt
László Böszörményi ESOP Rekursive Algorithmen - 18
Implementierung der Hilbert Kurve als Appletimport java.io.*;import java.awt.BorderLayout;import java.awt.Color;import java.applet.Applet;
public class Hilbert extends Applet {static final int W = 600, H = 600; // Breite und Höhe der Zeichenflächestatic final int EAST = 0, NORTH = 270, WEST = 180, SOUTH = 90;int u = W / 2; // Länge der Verbindungslinieint x = 0, y = 0; // Anfangsposition des StiftesColor [] colors = {Color.black, Color.blue, Color.cyan, Color.gray, Color.magenta};TurtleGraphicsPanel t;public void init() { // Methode init wird beim Laden des Applets aufgerufen
resize(W, H); // Zeichenfläche bestimment = new TurtleGraphicsPanel(); // Turtle-Instanz erzeugensetLayout(new BorderLayout());add("Center",t);
} // init
Inhalt der Hilbert.html Datei:<applet code="Hilbert.class" width=400 height=400></applet>Starten: appletviewer Hilbert.html
László Böszörményi ESOP Rekursive Algorithmen - 19
Implementierung der Hilbert Kurven – Ai, Bivoid A (int i) {
if (i > 0) {D(i - 1); t.turnTo(WEST); t.forward(u);A(i - 1); t.turnTo(SOUTH); t.forward(u);A(i - 1); t.turnTo(EAST); t.forward(u);B(i - 1);
} // if i} // A
void B (int i) {if (i > 0) {
C(i - 1); t.turnTo(NORTH); t.forward(u);B(i - 1); t.turnTo(EAST); t.forward(u);B(i - 1); t.turnTo(SOUTH); t.forward(u);A(i - 1);
} // if i} // B
László Böszörményi ESOP Rekursive Algorithmen - 20
Implementierung der Hilbert Kurven – Ci, Divoid C (int i) {
if (i > 0) {B(i - 1); t.turnTo(EAST); t.forward(u);C(i - 1); t.turnTo(NORTH); t.forward(u);C(i - 1); t.turnTo(WEST); t.forward(u);D(i - 1);
} // if i} // C
void D (int i) {if (i > 0) {
A(i - 1); t.turnTo(SOUTH); t.forward(u);D(i - 1); t.turnTo(WEST); t.forward(u);D(i - 1); t.turnTo(NORTH); t.forward(u);C(i - 1);
} // if i} // D
László Böszörményi ESOP Rekursive Algorithmen - 21
Implementierung der Hilbert Kurven – Startpublic void start() { // start wird von der Applet-Umgebung aufgerufen
t.clearScreen(); // Zeichenfläche löschent.hideTurtle(); // Turtle (ein kleines Dreieck) verstecken
for (int i = 1; i <= colors.length; i++) { x+= u / 2; // Anfangsposition für Kurvei
y+= u / 2; // Anfangsposition für Kurvei
t.setPen(x, y); // Stift auf die Anfangsposition t.setColor(colors[i - 1]); // Farbe für KurveiA(i); // Ai zeichnenu = u / 2; // Länge der Verbindungslinie setzen
} // for i
} // start} // Hilbert