Upload
votuong
View
224
Download
0
Embed Size (px)
Citation preview
Rekursive FunktionenOOPM, Ralf Lämmel
Um Rekursion zu verstehen, muss man vor
allem Rekursion verstehen.
http://www2.norwalk-city.k12.oh.us/wordpress/precalc/files/2009/05/mona-lisa-jmc.jpg
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Was ist Rekursion?
190
Eine Illustration von Rekursion
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Eine (rekursive) mathematische Definition von “!” (Fakultät)
n! =1, falls n = 0n * (n-1)!, falls n > 1
192
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Rekursive Berechnung von “!”
5! = 5 *
4! = 4 *
3! = 3 *
‣ 2! = 2 *- 1! = 1 *
- 0! = 1
193
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Rekursive Definition von “!” in Java
Umsetzung in Java:
194
// Assume n >= 0public static int factorial(int n) {
if (n == 0)return 1;
elsereturn n * factorial(n-1);
}
Siehe package algorithm.factorial
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Aufruf (nicht-) rekursiver Funktionen
195
public class Program {public static int divBy2(int x) {
return x / 2;}public static void main(String[] args) {
int x = 5;int y = divBy2(x);System.out.println(y);
}}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Die Realisierung eines Funktionsaufrufs entspricht dem Entfalten der Funktionsdefinition. Dabei werden formale Parameter durch aktuelle Parameter ersetzt.
196
int x = 5;int y = divBy2(x);System.out.println(y);
int x = 5;int y = x / 2;System.out.println(y);
Die Entfaltung ist statisch möglich für nichtrekursive Funktionen. Die Entfaltung ist notwendigerweise dynamisch (zur Laufzeit)
nötig für rekursive Funktionen.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Rekursive Funktionsdefinitionen
Teile der DefinitionBasisfall
Nichtrekursive BerechnungRekursiver Fall
Rekursive(r) Aufruf(e)Verwertung der (des) Aufrufe(s)‣ Nichtrekursive Berechnung
Die Auswahl der Teile erfolgt über Bedingung(en).Es kann auch mehre Fälle jeder Art geben.
197
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Rekursive Definition von “!”
198
// Assume n >= 0
public static int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n-1);
}
Basisfall
Rekursiver Fall
Abbruchbedingung
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
“!” mittels ternärem Operator
199
// Assume n >= 0
public static int factorial(int n) {
return (n == 0) ?
1
: n * factorial(n-1);
}
Basisfall
Rekursiver Fall
Abbruchbedingung
Angenommene Vorbedingung (oft
ausgelassen)
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Die Fibonacci-Folge
Anzahl neugeborener Kaninchen nach n Jahren:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
Rekursive Funktionsdefinition:fib(n) =
0, falls n = 01, falls n = 1fib(n-2) + fib(n-1), n >= 2
200
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Die Fibonacci-Folge
201
public static int fib(int n) {
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 2) + fib(n - 1);
}
1. Basisfall
2. Basisfall
2 rekursive Aufrufe
Siehe package algorithm.fibonacci
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Die Fibonacci-Folge (mit Verwendung des ternären Operators)
202
public static int fib(int n) {
return n == 0 ?
0
: n == 1 ?
1
: fib(n - 2) + fib(n - 1);
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Anmerkung zur Wahl rekursiver Definitionen
Folgende Attribute sind relativ:EleganzIn-/effizienz
Beispiel - Die Fibonacci-Folge: ‣ Exponentielle Explosion von Aufrufen‣ fib(n) benötigt nur 2 direkte Vorgänger.‣ Effizientere Formulierung darum naheliegend
OverheadStichwort Compiler-Optimierungen
203
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Umwandlung der ineffizienten, (binär-)rekursiven Fibonacci-Funktion in eine effiziente, entweder
(linear-)rekursive oder iterative Darstellung
204
Naive, rekursive FormulierungExponentielle Explosion von Aufrufen
Schlüsseleigenschaftfib(n) benötigt nur 2 direkte Vorgänger.
Effiziente, rekursive Formulierung Vorgänger werden als Parameter gereicht.
Effiziente, iterative Formulierung Vorgänger werden in Variablen gehalten.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Effiziente, rekursive Formulierung
205
public static int fib(int n) {return fib(n,0,1);
}
public static int fib(int n, int n1, int n2) {return n == 0 ? n1 : fib(n-1, n2, n1 + n2);
}
Wir überladen den Funktionsnamen. (Wir verwenden verschiedene Argumenttypen.)
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Effiziente, iterative Formulierung
206
public static int fib(int n) {int n1 = 0;int n2 = 1;for (; n != 0; n--) {
int m1 = n1;int m2 = n2;n1 = m2;n2 = m1 + m2;
}return n1;
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Türme von Hanoi
Annahmen:
N Scheiben verschiedener Durchmesser
Stapelung mit abnehmendem Durchmesser
3 Plätze: A, B, C
Ziel:
Bewegen aller Scheiben von A nach C
207
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Türme von Hanoi - Illustration
208
Startkonfiguration Move 1 Move 3Move 2
Move 4 Move 5 Move 6 Endkonfiguration
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Türme von Hanoi - Ansatz
Ziel: Verschiebung von N Scheiben von A nach C
Annahme: Verschieben von N-1 Scheiben möglich
Schritte:
Verschiebe N-1 Scheiben von A nach B
Verschiebe letzte (große) Scheibe von A nach C
Verschiebe N-1 Scheiben von B nach C
209
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Türme von Hanoi
210
public class Program {public static void move(int n, String from, String temp, String to) {
if (n == 0) return; move(n-1, from, to, temp); System.out.println(
"Move disc " + n + " from " + from + " to " + to); move(n-1, temp, from, to);
}public static void main(String[] args) {
move(3,"A","B","C");}
}
Move disc 1 from A to C Move disc 2 from A to B Move disc 1 from C to B Move disc 3 from A to C Move disc 1 from B to A Move disc 2 from B to C Move disc 1 from A to C
Siehe package algorithm.hanoi
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Formen von Rekursion
Primitive Rekursion
Allgemeine Rekursion
Endrekursion
Lineare Rekursion
Binäre Rekursion
Indirekte Rekursion
211
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Primitive RekursionFunktion über induktive Struktur von nat. ZahlenSchemavariablen: g, hSchema:f(n) =
g, falls n=0h(n,f(n-1)), sonst
212
public static int factorial(int n) {if (n == 0)
return 1;else
return n * factorial(n-1);}
Bestimme g und h!
Terminationist garantiert.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (Primitiv-rekursive Variante)
213
// Assume n >= 0public static boolean even(int n) {
return (n<=1) ? (n==0) : !even(n-1);}// Assume n >= 0public static boolean odd(int n) {
return (n<=1) ? (n==1) : !odd(n-1);}
Eine einfache Verallgemeinerung der primitiven Rekursion mit einem Basisfall > 0
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Allgemeine Rekursion
Keine Einschränkungen an RekursionBeispiel:
McCarty 91-er Funktion:f(n) =‣ n-10, falls x > 100‣ f(f(n+11)), sonst
Eigenschaften:‣ f(n) = 91 für alle n <= 101‣ f(n) = n - 10 für alle n > 101
214
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: McCarty 91-er Funktion
215
public static int f(int n) {
if (n > 100)
return n - 10;
else
return f(f(n+11));
}
f(n) = 91 für alle n <= 101f(n) = n - 10 für alle n > 101
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Endrekursion
Schemavariablen: g, p, r
Schema:
f(x) =
g(x), falls p(x)
f(r(x)), sonst
Bedeutung: iterative Implementation trivial
Endrekursive Formulierung oft relativ einfach
Beispiele: “!”, even/odd nach Anpassung
216
http://calvinlawson.files.wordpress.com/2009/02/tail-recursion.jpg
Der rekursive Aufruf ist die letzte Operation auf
jeder Ebene.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (Primitiv-rekursive Variante)
217
// Assume n >= 0public static boolean even(int n) {
return (n<=1) ? (n==0) : !even(n-1);}// Assume n >= 0public static boolean odd(int n) {
return (n<=1) ? (n==1) : !odd(n-1);}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (End-rekursive Variante)
218
// Assume n >= 0public static boolean even(int n) {
return (n<=1) ? (n==0) : even(n-2);}// Assume n >= 0public static boolean odd(int n) {
return (n<=1) ? (n==1) : odd(n-2);}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Umwandlung von Endrekursion in eine iterative Formulierung
Rekursiv: f(x) =g(x), falls p(x)f(r(x)), sonst
Iterativ: f(x) =Solange p(x) nicht gilt‣ x = r(x)
Gib g(x) zurück.
219
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (End-rekursive Variante)
220
public static boolean even(int n) {return (n<=1) ? (n==0) : even(n-2);
}
public static boolean even(int n) {while (!(n<=1))
n -= 2;return n==0;
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Umwandlung von primitiver nach End-Rekursion am Beispiel der Fakultät
(Verwendung eines Akkumulatorargumentes)
Primitive Rekursion: n! =
1, falls n = 0n * (n-1)!, sonst
Endrekursion: n! = factorial(1,n)factorial(x,n) =
x, falls n = 0factorial(x*n, n-1), sonst
221
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: Fakultät (End-rekursive Variante)
222
public static int factorial(int n) {return (n == 0) ? 1 : n * factorial(n-1);
}
public static int factorial(int n) {return factorial(1,n);
}public static int factorial(int x, int n) {
return (n==0) ? x : factorial(n*x,n-1);}
Wir überladen den Funktionsnamen. (Wir verwenden verschiedene
Argumenttypen.)
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Lineare Rekursion
Verallgemeinerung von:Primitive RekursionEndrekursion
Schemavariablen: g, p, h, rSchema: f(x) =
g(x), falls p(x)h(x,f(r(x))), sonst
Einfache Verallgemeinerung: f kann mehrmals referenziert werden ...... aber nur einmal ausgewertet werden.
223
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Binäre Rekursion
Verallgemeinerung von linearer Rekursion
Zwei, verschiedenartige rekursive Aufrufe.
Beide Aufrufe fallen auch dynamisch an.
Beispiel: Die Fibonacci-Funktion
224
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Indirekte RekursionEine Funktion ruft sich nicht (nur) direkt selbst auf.Stattdessen sind mehrere Funktionen beteiligt.Indirekte Rekursion erleichtert Modularisierung.Beispiel:
even(n) =true, falls n = 0odd(n-1), sonst
odd(n) =false, falls n = 0even(n-1), sonst
225
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (Indirekt-rekursive Variante)
226
// Assume n >= 0public static boolean even(int n) {
if (n==0)return true;
elsereturn odd(n-1);
}// Assume n >= 0public static boolean odd(int n) {
if (n==0)return false;
elsereturn even(n-1);
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Beispiel: even/odd (Kompaktere indirekt-rekursive Variante)
227
// Assume n >= 0public static boolean even(int n) {
return (n==0) || odd(n-1);}// Assume n >= 0public static boolean odd(int n) {
return !(n==0) && even(n-1);}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Zusammenfassung Rekursive Definitionen sind oft elegant.Rekursion kann Kosten verursachen.Konvertierung rekursiv nach iterativ
Prinzipiell immer möglichPraktisch oft elegant möglich
Ausblick Nächstes Mal: Einfache Sortieralgorithmen
Anwendung von FeldernAnwendung von rekursiven Funktionen