Um Rekursion zu verstehen, muss man vor allem Rekursion ...laemmel/oopm/slides/recfuns.pdf · (C)...

Preview:

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

Recommended