Was bisher geschah I Definition Algorithmus I Eigenschaften von Algorithmen I Spezifikation von Berechnungsproblemen I Instanzen von Berechnungsproblemen I Spezifikation des Sortierproblemes I Korrektheit von Algorithmen I Algorithmenentwurf: Spezifikation, Entwurf, Verifikation I Laufzeit von Algorithmen I O-Notation 43 Rekursive Algorithmen Basisfall (Induktionsanfang): direkte Lösung Rekursionsschritt (Induktionsschritt): Funktionswert wird aus den Funktionswerten derselben Funktion mit anderen (einfacheren) Argumenten berechnet Beispiele: I Fakultät 1 falls n = 0 n! = n(n − 1)! sonst I I I Fibonacci-Funktion falls n = 0 0 1 falls n = 1 f (n) = f (n − 1) + f (n − 2) sonst I Ackermann-Funktion falls x = 0 y +1 a(x − 1, 1) falls y = 0 und x > 0 a(x, y ) = a(x − 1, a(x, y − 1)) sonst 44 Funktionale Programmiersprache Haskell (statt Pseudocode) Haskell-Interpreter zum Ausprobieren: I ghc / ghci (http://www.haskell.org/ghc/) I Hugs (www.haskell.org/hugs/) fac :: Int -> Int fac 0 = 1 fac n = n * fac (n-1) fib fib fib fib :: Int -> Int 0 = 1 1 = 1 n = fib (n-1) + fib (n-2) ack ack ack ack :: Int -> 0 y = y + x 0 = ack x y = ack Int -> Int 1 (x-1) 1 (x-1) (ack x (y-1)) 45 Laufzeit-Berechnung für n! Algorithmus : fac Eingabe : x Ausgabe : y Spezifikation: V: x ∈ wenn x = 0 dann y ←1 sonst y ← x fac(x − 1) N N: y = x! T (n) Rekurrenz T (0) = 1 T (n) = T (n − 1) + 1 = = T (n − 1) + 1 (T (n − 2) + 1) + 1 = T (n − 2) + 2 = T (n − 3) + 3 ... = Laufzeit O(n) T (n − n) + n = T (0) + n = 1 + n 46 Divide-and-Conquer-Verfahren (teile und herrsche) Idee: rekursive Zerlegung des Problemes in Teilprobleme bis zum Basisfall Grundprinzip: Teilung des Problems P in n Teilprobleme Pi Lösung aller Teilprobleme Pi (Rekursion) Kombination der Lösungen der Teilprobleme Pi zu einer Lösung des Problems P (Baumstruktur) 47 Beispiel: Suche in sortierten Folgen Spezifikation des Suchproblemes in sortierten Folgen: V: Eingaben x = (x1 , . . . , xn ) aufsteigend sortiert, y N: Ausgabe: I Ja, falls ein i ∈ {1, . . . , n} mit xi = y existiert, I sonst Nein Algorithmus : Suche3 Eingabe : (x1 , . . . , xn ), y und l, r ∈ {1, . . . , n} (Bereichsgrenzen Ausgabe : gefunden wenn l > r dann gefunden ← Nein sonst m ← b(l + r )/2c wenn xm = y dann gefunden ← Ja sonst wenn xm < y dann Suche3(x, y , m + 1, r ) sonst Suche3(x, y , l, m − 1) 48 Laufzeit: Suche in sortierten Folgen T (0) = a T (n) = T (bn/2c) + b für n = 2k , also k = log n: T (n) = T (n/21 ) + b = T (n/22 ) + b + b = T (n/23 ) + 3b ... = T (n/2k ) + kb = a + k b Laufzeit: O(log n) 49 Klassisches Beispiel: Türme von Hanoi I Positionen A, B.C I n Scheiben {1, . . . , n} der Größen 1, . . . , n I Bedingung: Scheibe i darf nur dann auf Scheibe j liegen, wenn i < j zulässige Bewegungen (zusammen ein Zug): I I I Nimm die obere Scheibe i von Position x ∈ {A, B, C} und Lege diese Scheibe i auf Position y ∈ {A, B, C}, sofern damit auf Position y die Bedingung nicht verletzt wird I Startsituation: Auf Position A ein Turm aus n (zulässig) gestapelten Scheiben, Positionen B, C leer I Zielsituation: Auf Position B ein Turm aus n (zulässig) gestapelten Scheiben, Positionen A, C leer gesucht: Folge von Schritten vom Start- zum Zielzustand 50 Türme von Hanoi: rekursiver Algorithmus Verschiebe einen Hanoi-Turm der Höhe n von Position A zu Position B, falls nötig, unter Verwendung der Position C: Spezifikation: V: (x : n, y : 0, z : 0), l = () N: (x : 0, y : n, z : 0), l enthält die Folge aller Züge (Start- und Zielposition der bewegten Scheibe) Algorithmus Hanoi(x, y , z): Basisfall: n = 0 (keine Bewegung) Rekursionsschritt: 1. die oberen n − 1 Scheiben von Position x zu Position z verschieben, falls nötig, unter Verwendung der Position y (rekursiv), 2. Verschiebe die größte Scheibe von Position x zu Position y , Zug (x → y ) an l anhängen 3. die n − 1 Scheiben von Position z zu Position y verschieben, falls nötig, unter Verwendung der Position x (rekursiv). 51 Türme von Hanoi in Haskell Datentyp für die Positionen: data Platz = A | B | C deriving Show hanoi :: Int -> Platz -> Platz -> Platz -> [ ( Platz , Platz ) ] hanoi 0 von nach hilf = [] hanoi n von nach hilf = hanoi ( n - 1 ) von hilf nach ++ [ ( von , nach ) ] ++ hanoi ( n - 1 ) hilf nach von 52 Türme von Hanoi: Laufzeit Laufzeit: Anzahl der Bewegungen T (0) = 0 T (n) = 2T (n − 1) + 1 T (n) = 2T (n − 1) + 1 = 2(2T (n − 2) + 1) + 1 = 4T (n − 2) + 3 = 2(2(2T (n − 3) + 1) + 1) + 1 = 8T (n − 2) + 7 ... = 2n T (0) + n X 2i−1 = 2n − 1 i=0 Laufzeit des Algorithmus: O(2n ) 53 Häufig vorkommende Rekurrenzen T (n) = T (n − 1) + 1 T (n) = T (n − 1) + n T (n) = T (n/2) + 1 T (n) = T (n/2) + n T (n) = 2T (n/2) + 1 T (n) = 2T (n − 1) + 1 . . . = T (0) + n . . . = T (0) + n2 . . . = T (0) + log n . . . = T (0) + n log n . . . = 2log n T (0) + 2log n log n O(n) O(n2 ) O(log n) O(n log n) O(n log n) O(2n ) 54