9. Rekursion - lec.inf.ethz.ch · Die Turme¨ von Hanoi - Rekursiver Losungsansatz¨ Links Mitte...

Post on 25-Aug-2019

214 views 0 download

Transcript of 9. Rekursion - lec.inf.ethz.ch · Die Turme¨ von Hanoi - Rekursiver Losungsansatz¨ Links Mitte...

9. Rekursion

Mathematische Rekursion, Terminierung, der Aufrufstapel,Beispiele, Rekursion vs. Iteration

196

Experiment: Die Turme von Hanoi

Links Mitte Rechts

197

Experiment: Die Turme von Hanoi

Links Mitte Rechts

197

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - So gehts!

Links Mitte Rechts

198

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Mal angenommen, wirwüssten wie man ...

... drei Scheiben von einemStapel zum anderen bringt ...... dann ist es sehr einfach!

... und hopp!

199

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir dreiScheiben bewegen?

Mal angenommen, wirwüssten wie man ...

... zwei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

200

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir dreiScheiben bewegen?

Mal angenommen, wirwüssten wie man ...

... zwei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

200

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir dreiScheiben bewegen?

Mal angenommen, wirwüssten wie man ...

... zwei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

200

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir dreiScheiben bewegen?

Mal angenommen, wirwüssten wie man ...

... zwei Scheiben von einemStapel zum anderen bringt ...

... dann ist es sehr einfach!

200

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir zweiScheiben bewegen?

Wir wissen, wie man ...

... eine Scheiben von einemStapel zum anderen bringt!

Alles einfach! Der Restgeht im gleichen Stilweiter...

201

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir zweiScheiben bewegen?

Wir wissen, wie man ...

... eine Scheiben von einemStapel zum anderen bringt!

Alles einfach! Der Restgeht im gleichen Stilweiter...

201

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir zweiScheiben bewegen?

Wir wissen, wie man ...

... eine Scheiben von einemStapel zum anderen bringt!

Alles einfach! Der Restgeht im gleichen Stilweiter...

201

Die Turme von Hanoi - Rekursiver Losungsansatz

Links Mitte Rechts

Doch wie können wir zweiScheiben bewegen?

Wir wissen, wie man ...

... eine Scheiben von einemStapel zum anderen bringt!

Alles einfach! Der Restgeht im gleichen Stilweiter...

201

Mathematische Rekursion

Viele mathematische Funktionen sind sehr natürlich rekursivdefinierbar.

Das heisst, die Funktion erscheint in ihrer eigenen Definition.

n! =

{1, falls n ≤ 1

n · (n− 1)!, andernfalls

202

Mathematische Rekursion

Viele mathematische Funktionen sind sehr natürlich rekursivdefinierbar.Das heisst, die Funktion erscheint in ihrer eigenen Definition.

n! =

{1, falls n ≤ 1

n · (n− 1)!, andernfalls

202

Rekursion in Java: Genauso!

n! =

{1 falls n ≤ 1

n · (n− 1)!, andernfalls

public static int fakultaet ( int n) {if (n <= 1) {

return 1;} else {

return n ∗ fakultaet (n−1);}

}

n!⇔ fakultaet(n)n− 1!⇔ fakultaet(n−1)

203

Rekursion in Java: Genauso!

n! =

{1 falls n ≤ 1

n · (n− 1)!, andernfalls

public static int fakultaet ( int n) {if (n <= 1) {

return 1;} else {

return n ∗ fakultaet (n−1);}

}

n!⇔ fakultaet(n)n− 1!⇔ fakultaet(n−1)

203

Unendliche Rekursion

ist so schlecht wie eine Endlosschleife. . .

. . . nur noch schlimmer (“verbrennt” Zeit und Speicher)

Beispiel: f()→ f()→ f()→ f()→ . . . stack overflow

public static void f () {f ();

}

Mir san mir.

Bayerisches Lebensmotto

204

Unendliche Rekursion

ist so schlecht wie eine Endlosschleife. . .. . . nur noch schlimmer (“verbrennt” Zeit und Speicher)

Beispiel: f()→ f()→ f()→ f()→ . . . stack overflow

public static void f () {f ();

}

Mir san mir.

Bayerisches Lebensmotto

204

Unendliche Rekursion

ist so schlecht wie eine Endlosschleife. . .. . . nur noch schlimmer (“verbrennt” Zeit und Speicher)

Beispiel: f()→ f()→ f()→ f()→ . . . stack overflow

public static void f () {f ();

}

Mir san mir.

Bayerisches Lebensmotto

204

Unendliche Rekursion

ist so schlecht wie eine Endlosschleife. . .. . . nur noch schlimmer (“verbrennt” Zeit und Speicher)

Beispiel: f()→ f()→ f()→ f()→ . . . stack overflow

public static void f () {f ();

}

Mir san mir.

Bayerisches Lebensmotto204

Rekursive Funktionen: Terminierung

Wie bei Schleifen brauchen wir

Fortschritt Richtung Terminierung

fakultaet(n)terminiert sofort für n ≤ 1, andernfalls wird die Funktion rekursiv mitArgument < n aufgerufen.

“n wird mit jedem Aufruf kleiner.”

205

Rekursive Funktionen: Terminierung

Wie bei Schleifen brauchen wir

Fortschritt Richtung Terminierung

fakultaet(n)terminiert sofort für n ≤ 1, andernfalls wird die Funktion rekursiv mitArgument < n aufgerufen.

“n wird mit jedem Aufruf kleiner.”

205

Rekursive Funktionen: Terminierung

Wie bei Schleifen brauchen wir

Fortschritt Richtung Terminierung

fakultaet(n)terminiert sofort für n ≤ 1, andernfalls wird die Funktion rekursiv mitArgument < n aufgerufen.

“n wird mit jedem Aufruf kleiner.”

205

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){

if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Aufruf von fakultaet(4)

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){// n = 4if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Initialisierung des formalen Arguments

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){// n = 4if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Auswertung des Rückgabeausdrucks

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){// n = 4if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Rekursiver Aufruf mit Argument n− 1, also 3

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){// n = 3if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Initialisierung des formalen Arguments

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){// n = 3if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Initialisierung des formalen Arguments

Es gibt jetzt zwei n. Das von fac(4) und das von fac(3)

206

Rekursive Funktionen: Auswertung

Beispiel: fakultaet(4)

public static int fakultaet (int n){

if (n <= 1) return 1;return n * fakultaet(n-1); // n > 1

}

Initialisierung des formalen Arguments

Es wird mit dem n des aktuellen Aufrufs gearbeitet: n = 3

206

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3)

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3)

fakultaet(2)

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1

n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3)

fakultaet(2)

fakultaet(1)

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1

n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3)

fakultaet(2)

fakultaet(1) 1

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3)

fakultaet(2) 2

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4)

fakultaet(3) 6

207

Der Aufrufstapel

Bei jedem Funktionsaufruf:

Wert des Aufrufarguments kommt aufeinen Stapel

Es wird immer mit dem obersten Wertgearbeitet

Am Ende des Aufrufs wird der obersteWert wieder vom Stapel gelöscht

System.out.println(fakultaet(4))

n = 4

n = 3

n = 2

n = 1n = 1 1! = 1

n = 2 2 · 1! = 2

n = 3 3 · 2! = 6

n = 4 4 · 3! = 24

fakultaet(4) 24

207

Euklidischer Algorithmus

findet den grössten gemeinsamen Teiler gcd(a, b) zweiernatürlicher Zahlen a und b

basiert auf folgender mathematischen Rekursion:

gcd(a, b) =

{a, falls b = 0

gcd(b, a mod b), andernfalls

208

Euklidischer Algorithmus

findet den grössten gemeinsamen Teiler gcd(a, b) zweiernatürlicher Zahlen a und bbasiert auf folgender mathematischen Rekursion:

gcd(a, b) =

{a, falls b = 0

gcd(b, a mod b), andernfalls

208

Euklidischer Algorithmus in Java

gcd(a, b) =

{a, falls b = 0

gcd(b, a mod b), andernfalls

public static int gcd(int a, intb){if (b == 0) {

return a;} else {

return gcd(b, a%b);}

}

209

Euklidischer Algorithmus in Java

gcd(a, b) =

{a, falls b = 0

gcd(b, a mod b), andernfalls

public static int gcd(int a, intb){if (b == 0) {

return a;} else {

return gcd(b, a%b);}

}

Terminierung: a mod b < b, alsowird b in jedem rekursiven Aufrufkleiner.

209

Fibonacci-Zahlen

Fn :=

0, falls n = 0

1, falls n = 1

Fn−1 + Fn−2, falls n > 1

Resultat: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 . . .

210

Fibonacci-Zahlen

Fn :=

0, falls n = 0

1, falls n = 1

Fn−1 + Fn−2, falls n > 1

Resultat: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 . . .

210

Fibonacci-Zahlen in Zurich

211

Fibonacci-Zahlen in Java

Fn :=

0, falls n = 0

1, falls n = 1

Fn−1 + Fn−2, falls n > 1

public static int fib (int n){if (n == 0 || n == 1){

return n;} else {

return fib (n−1) + fib(n−2);}

}

212

Fibonacci-Zahlen in Java

Fn :=

0, falls n = 0

1, falls n = 1

Fn−1 + Fn−2, falls n > 1

public static int fib (int n){if (n == 0 || n == 1){

return n;} else {

return fib (n−1) + fib(n−2);}

}

Korrektheit und Terminierungsind klar, aber...

212

Fibonacci-Zahlen in Java

Laufzeitfib(50) dauert „ewig”, denn es berechnetF48 2-mal, F47 3-mal, F46 5-mal, F45 8-mal, F44 13-mal,F43 21-mal ... F1 ca. 109 mal (!)

public static int fib (int n){if (n == 0 || n == 1){

return n;} else {

return fib (n−1) + fib(n−2);}

}

212

Schnelle Fibonacci-Zahlen

Idee:

Berechne jede Fibonacci-Zahl nur einmal, in der ReihenfolgeF0, F1, F2, . . . , Fn!

Merke dir jeweils die zwei letzten berechneten Zahlen (Variablen aund b)!Berechne die nächste Zahl als Summe von a und b!

213

Schnelle Fibonacci-Zahlen

Idee:

Berechne jede Fibonacci-Zahl nur einmal, in der ReihenfolgeF0, F1, F2, . . . , Fn!Merke dir jeweils die zwei letzten berechneten Zahlen (Variablen aund b)!

Berechne die nächste Zahl als Summe von a und b!

213

Schnelle Fibonacci-Zahlen

Idee:

Berechne jede Fibonacci-Zahl nur einmal, in der ReihenfolgeF0, F1, F2, . . . , Fn!Merke dir jeweils die zwei letzten berechneten Zahlen (Variablen aund b)!Berechne die nächste Zahl als Summe von a und b!

213

Schnelle Fibonacci-Zahlen in Java

public static int fib (int n)\{if (n == 0) return 0;if (n <= 2) return 1;int a = 1; // F(1)int b = 1; // F(2)for ( int i = 3; i <= n; ++i){

int a_old = a; // F(i−2)a = b; // F(i−1)b += a_old; // F(i−1) += F(i−2) −> F(i)

}return b;

}

214

10. Suchen

215

Das Suchproblem

Gegeben

Menge von Datensätzen.

BeispieleTelefonverzeichnis, Wörterbuch, Symboltabelle

Jeder Datensatz hat einen Schlüssel k.Schlüssel sind vergleichbar: eindeutige Antwort auf Frage k1 ≤ k2für Schlüssel k1, k2.

Aufgabe: finde Datensatz nach Schlüssel k.

216

Suche in Array

Gegeben

Array A mit n Elementen (A[1], . . . , A[n]).Schlüssel b

Gesucht: Index k, 1 ≤ k ≤ n mit A[k] = b oder ”nicht gefunden”.

10

4

20

2

22

1

24

6

28

9

32

3

35

5

38

8

41

10

42

7

217

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.Schlimmstenfalls n Vergleiche.Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.

Schlimmstenfalls n Vergleiche.Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.Schlimmstenfalls n Vergleiche.

Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.Schlimmstenfalls n Vergleiche.Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.Schlimmstenfalls n Vergleiche.Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Lineare Suche

Durchlaufen des Arrays von A[1] bis A[n].

Bestenfalls 1 Vergleich.Schlimmstenfalls n Vergleiche.Annahme: Jede Anordnung der n Schlüssel istgleichwahrscheinlich. Erwartete Anzahl Vergleiche:

1

n

n∑i=1

i =n+ 1

2.

218

Suche in sortierten Array

Gegeben

Sortiertes Array A mit n Elementen (A[1], . . . , A[n]) mitA[1] ≤ A[2] ≤ · · · ≤ A[n].Schlüssel b

Gesucht: Index k, 1 ≤ k ≤ n mit A[k] = b oder ”nicht gefunden”.

10

1

20

2

22

3

24

4

28

5

32

6

35

7

38

8

41

9

42

10

219

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Divide and Conquer!Suche b = 23.

101

202

223

244

285

326

357

388

419

4210

b < 28

101

202

223

244

285

326

357

388

419

4210

b > 20

223

244

285

101

202

326

357

388

419

4210

b > 22

244

101

202

223

285

326

357

388

419

4210

b < 24

244

101

223

202

285

326

357

388

419

4210

erfolglos

220

Binarer Suchalgorithmus BSearch(A,b,l,r)

Input : Sortiertes Array A von n Schlusseln. Schlussel b. Bereichsgrenzen1 ≤ l ≤ r ≤ n oder l > r beliebig.

Output : Index des gefundenen Elements. 0, wenn erfolglos.m← b(l + r)/2cif l > r then // erfolglose Suche

return 0else if b = A[m] then// gefunden

return melse if b < A[m] then// Element liegt links

return BSearch(A, b, l,m− 1)else // b > A[m]: Element liegt rechts

return BSearch(A, b,m+ 1, r)

221

Analyse (Schlimmster Fall)Rekurrenz (n = 2k)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Teleskopieren:

T (n) = T(n

2

)+ c

= T(n

4

)+ 2c

= T(n

2i

)+ i · c

= T(nn

)+ log2 n · c.

⇒ Annahme: T (n) = d+ c log2 n

222

Analyse (Schlimmster Fall)Rekurrenz (n = 2k)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Teleskopieren:

T (n) = T(n

2

)+ c = T

(n4

)+ 2c

= T(n

2i

)+ i · c

= T(nn

)+ log2 n · c.

⇒ Annahme: T (n) = d+ c log2 n

222

Analyse (Schlimmster Fall)Rekurrenz (n = 2k)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Teleskopieren:

T (n) = T(n

2

)+ c = T

(n4

)+ 2c

= T(n

2i

)+ i · c

= T(nn

)+ log2 n · c.

⇒ Annahme: T (n) = d+ c log2 n

222

Analyse (Schlimmster Fall)Rekurrenz (n = 2k)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Teleskopieren:

T (n) = T(n

2

)+ c = T

(n4

)+ 2c

= T(n

2i

)+ i · c

= T(nn

)+ log2 n · c.

⇒ Annahme: T (n) = d+ c log2 n

222

Analyse (Schlimmster Fall)Rekurrenz (n = 2k)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Teleskopieren:

T (n) = T(n

2

)+ c = T

(n4

)+ 2c

= T(n

2i

)+ i · c

= T(nn

)+ log2 n · c.

⇒ Annahme: T (n) = d+ c log2 n222

Analyse (Schlimmster Fall)

T (n) =

{d falls n = 1,T (n/2) + c falls n > 1.

Vermutung : T (n) = d+ c · log2 n

Beweis durch Induktion:

Induktionsanfang: T (1) = d.Hypothese: T (n/2) = d+ c · log2 n/2

Schritt (n/2→ n)

T (n) = T (n/2) + c = d+ c · (log2 n− 1) + c = d+ c log2 n.

223

Resultat

TheoremDer Algorithmus zur binären sortierten Suche benötigt Θ(log n)Elementarschritte.

224

Iterativer binarer Suchalgorithmus

Input : Sortiertes Array A von n Schlusseln. Schlussel b.Output : Index des gefundenen Elements. 0, wenn erfolglos.l← 1; r ← nwhile l ≤ r do

m← b(l + r)/2cif A[m] = b then

return melse if A[m] < b then

l← m+ 1else

r ← m− 1

return 0;

225

Korrektheit

Algorithmus bricht nur ab, falls A leer oder b gefunden.

Invariante: Falls b in A, dann im Bereich A[l, ..., r]

Beweis durch Induktion

Induktionsanfang: b ∈ A[1, .., n] (oder nicht)Hypothese: Invariante gilt nach i SchrittenSchritt:b < A[m]⇒ b ∈ A[l, ..,m− 1]b > A[m]⇒ b ∈ A[m+ 1, .., r]

226

11. Auswahlen

227

Min und Max

? Separates Finden von Minimum und Maximum in(A[1], . . . , A[n]) benötigt insgesamt 2n Vergleiche. (Wie) geht es mitweniger als 2n Vergleichen für beide gemeinsam?

! Es geht mit 32N Vergleichen: Vergleiche jeweils 2 Elemente und

deren kleineres mit Min und grösseres mit Max.

228

Min und Max

? Separates Finden von Minimum und Maximum in(A[1], . . . , A[n]) benötigt insgesamt 2n Vergleiche. (Wie) geht es mitweniger als 2n Vergleichen für beide gemeinsam?

! Es geht mit 32N Vergleichen: Vergleiche jeweils 2 Elemente und

deren kleineres mit Min und grösseres mit Max.

228

Das Auswahlproblem

Eingabe

Unsortiertes Array A = (A1, . . . , An) paarweise verschiedenerWerteZahl 1 ≤ k ≤ n.

Ausgabe: A[i] mit |{j : A[j] < A[i]}| = k − 1

Spezialfällek = 1: Minimum: Algorithmus mit n Vergleichsoperationen trivial.k = n: Maximum: Algorithmus mit n Vergleichsoperationen trivial.k = bn/2c: Median.

229

Ansatze

Wiederholt das Minimum entfernen / auslesen: O(k · n).Median: O(n2)

Sortieren (kommt bald): O(n log n)

Pivotieren O(n) !

230

Ansatze

Wiederholt das Minimum entfernen / auslesen: O(k · n).Median: O(n2)

Sortieren (kommt bald): O(n log n)

Pivotieren O(n) !

230

Ansatze

Wiederholt das Minimum entfernen / auslesen: O(k · n).Median: O(n2)

Sortieren (kommt bald): O(n log n)

Pivotieren O(n) !

230

Ansatze

Wiederholt das Minimum entfernen / auslesen: O(k · n).Median: O(n2)

Sortieren (kommt bald): O(n log n)

Pivotieren O(n) !

230

Pivotieren

1 Wähle ein Element p als Pivotelement2 Teile A in zwei Teile auf, den Rang von p bestimmend.3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

231

Pivotieren

1 Wähle ein Element p als Pivotelement

2 Teile A in zwei Teile auf, den Rang von p bestimmend.3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

p

231

Pivotieren

1 Wähle ein Element p als Pivotelement2 Teile A in zwei Teile auf, den Rang von p bestimmend.

3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

> ≤ ≤ > > ≤ ≤ > ≤p

231

Pivotieren

1 Wähle ein Element p als Pivotelement2 Teile A in zwei Teile auf, den Rang von p bestimmend.

3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

>≤ ≤ > >≤ ≤ >≤p

231

Pivotieren

1 Wähle ein Element p als Pivotelement2 Teile A in zwei Teile auf, den Rang von p bestimmend.

3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

>≤ ≤ > >≤ ≤ >≤p p≤

r1 n

231

Pivotieren

1 Wähle ein Element p als Pivotelement2 Teile A in zwei Teile auf, den Rang von p bestimmend.3 Rekursion auf dem relevanten Teil. Falls k = r, dann gefunden.

>≤ ≤ > >≤ ≤ >≤p p≤

r1 n

231

Algorithmus Partition(A[l..r], p)

Input : Array A, welches den Sentinel p im Intervall [l, r] mindestens einmal enthalt.Output : Array A partitioniert in [l..r] um p. Ruckgabe der Position von p.while l < r do

while A[l] < p dol← l + 1

while A[r] > p dor ← r − 1

swap(A[l], A[r])if A[l] = A[r] then

l← l + 1

return l-1

232

Korrektheit: Invariante

Invariante I: Ai ≤ p ∀i ∈ [0, l), Ai > p ∀i ∈ (r, n], ∃k ∈ [l, r] : Ak = p.while l < r do

while A[l] < p dol← l + 1

while A[r] > p dor ← r − 1

swap(A[l], A[r])if A[l] = A[r] then

l← l + 1

return l-1

I

I und A[l] ≥ p

I und A[r] ≤ pI und A[l] ≤ p ≤ A[r]

I

233

Korrektheit: Fortschritt

while l < r dowhile A[l] < p do

l← l + 1

while A[r] > p dor ← r − 1

swap(A[l], A[r])if A[l] = A[r] then

l← l + 1

return l-1

Fortschritt wenn A[l] < p

Fortschritt wenn A[r] > p

Fortschritt wenn A[l] > p oder A[r] < p

Fortschritt wenn A[l] = A[r] = p

234

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(?)

p1

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(?)

p1 p2

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(?)

p1 p2 p3

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(?)

p1 p2 p3 p4

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(n2)

p1 p2 p3 p4 p5

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

Wahl des PivotsDas Minimum ist ein schlechter Pivot: worst Case Θ(n2)

p1 p2 p3 p4 p5

Ein guter Pivot hat linear viele Elemente auf beiden Seiten.

p

≥ ε · n ≥ ε · n

235

AnalyseUnterteilung mit Faktor q (0 < q < 1): zwei Gruppen mit q · n und(1− q) · n Elementen (ohne Einschränkung q ≥ 1− q).

T (n) ≤ T (q · n) + c · n

= c · n+ q · c · n+ T (q2 · n) = ... = c · nlogq(n)−1∑

i=0

qi + T (1)

≤ c · n∞∑i=0

qi︸ ︷︷ ︸geom. Reihe

= c · n · 1

1− q= O(n)

236

Wie bekommen wir das hin?Der Zufall hilft uns (Tony Hoare, 1961). Wähle in jedem Schritt einenzufälligen Pivot.

14

14

12

schlecht schlechtgute Pivots

Wahrscheinlichkeit für guten Pivot nach einem Versuch: 12 =: ρ.

Wahrscheinlichkeit für guten Pivot nach k Versuchen: (1− ρ)k−1 · ρ.

Erwartungswert der geometrischen Verteilung: 1/ρ = 2237

Algorithmus Quickselect (A[l..r], i)Input : Array A der Lange n. Indizes 1 ≤ l ≤ i ≤ r ≤ n, so dass fur alle

x ∈ A[l..r] gilt, dass |{j|A[j] ≤ x}| ≥ l und |{j|A[j] ≤ x}| ≤ r.Output : Partitioniertes Array A, so dass |{j|A[j] ≤ A[i]}| = iif l=r then return;repeat

wahle zufalligen Pivot x ∈ A[l..r]p← lfor j = l to r do

if A[j] ≤ x then p← p+ 1

until l+r4≤ p ≤ 3(l+r)

4m← Partition(A[l..r], x)if i < m then

quickselect(A[l..m], i)else

quickselect(A[m..r], i)238