Funktionale Programmierung ALP I Induktion und Rekursion SS 2011 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda Funktionale Programmierung Vollständige Induktion Die Vollständige Induktion ist eine mathematische Beweistechnik, die auf die Menge der natürlichen Zahlen spezialisiert ist. Vorgehensweise: 1. Induktionsanfang Text Man zeigt die Behauptung für k=0 oder k=1 bzw. Anfangswert. 2. Induktionsschritt Man nimmt an, die Aussage sei für wahr und zeigt damit, dass die Aussage für k+1 wahr ist. Wenn beide Schritte erfolgreich durchgeführt wurden, ist die Behauptung für alle natürlichen Zahlen gezeigt. Prof. Dr. Margarita Esponda Funktionale Programmierung Vollständige Induktion Beispiel: Vermutung: Die Summe der ersten n ungeraden Zahlen ist gleich n2 n 2 2i − 1 = n ∑ i =1 Motivation: 1 = 12 1+ 3 = 22 1+ 3+ 5 = 32 1+ 3+ 5+ 7 = 42 ... Prof. Dr. Margarita Esponda … Funktionale Programmierung Vollständige Induktion Beweis per Vollständiger Induktion: Induktionsanfang: n 2 2 ⋅1 − 1 = 1 = 1 ∑ für n=1 Induktionsschritt: i =1 wir nehmen an, dass für n=k dann: k +1 k 2 2i − 1 = k ∑ i =1 2 2i − 1 = k + 2(k + 1) − 1 ∑ i =1 = k2 + 2 ⋅ k + 2 − 1 = k2 + 2 ⋅ k + 1 daraus folgt, dass die Vermutung = (k + 1)2 für alle Prof. Dr. Margarita Esponda n ∈ gilt. Funktionale Programmierung Vollständige Induktion Weitere Beispiele an der Tafel! Prof. Dr. Margarita Esponda Funktionale Programmierung Strukturelle Induktion Die Strukturelle Induktion ist eine allgemeinere Form der Vollständigen Induktion. Mit diesem mathematischen Beweisverfahren lassen sich Aussagen über die Elemente von rekursiv aufgebauten Datenmengen wie zum Beispiel Listen, Bäumen oder Graphen beweisen. Die Datenmengen, die damit behandelt werden, müssen aus einer endlichen Anzahl von Konstruktionsschritten aus Grundelementen entstehen. Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Listen Vorgehensweise: 1. Induktionsanfang Man zeigt eine bestimmte Eigenschaft P für die leere Liste [] 2. Induktionsschritt Man zeigt die Eigenschaft P(x:xs) unter der Annahme, dass P(xs) gilt. Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Listen Beispiel: Nehmen wir an, wir möchten zeigen, dass die Konkatenation über Listen assoziativ ist. Definition von (++): (++) :: [a] -> [a] -> [a] (++) [] ys = ys (++) (x:xs) ys = x : (++) xs ys Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Listen Behauptung: Für alle Listen xs, zs und ys über den Datentyp a gilt die Assoziativität-Eigenschaft: (++) ((++) xs ys) zs = (++) xs ((++) ys zs) oder (xs ++ ys) ++ zs = xs ++ (ys ++ zs) Beweis: (Induktion über xs) Induktionsanfang: (++) ((++) [] ys) zs) = (++) ys zs = (++) [] ((++) ys zs) (++).1 (++).1 Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Listen Induktionsschritt: (++) (x:xs) ((++) ys zs) (++) (x:xs) ((++) ys zs) ? = (++) ((++) (x:xs) ys) zs = x : (++) xs ((++) ys zs) = x : (++) ((++) xs ys) zs (++).2 Induktionsannahme = (++) (x:((++) xs ys)) zs = (++) ((++) (x:xs) ys) zs (++).2 (++).2 weitere Beispiele an der Tafel . . . Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Bäume Definition: a) Ein einzelner Blatt-Knoten ist ein Baum o b) Falls t1, t2,…,tm Bäume sind, dann ist ihre Verknüpfung unter einem Knoten o auch ein Baum (o t1, t2,…,tm ) o t1 t2 . . . tm Ein Baum ist balanciert, falls er ein Blatt oder von der Form (o t1, t2,…,tm) ist, wobei t1, t2,…,tm balanciert und von derselben Tiefe sind. Prof. Dr. Margarita Esponda Funktionale Programmierung Induktion über Bäume Behauptung: Ein balancierter m-Baum (Baum mit maximal m Kindern pro Knoten) mit m>0 und Tiefe n hat Knoten Motivation: o Tiefe 0 Tiefe 1 Tiefe 2 Tiefe n Prof. Dr. Margarita Esponda o o o t1 t2 . . .tm t1 t2 . . . tm Funktionale Programmierung Induktion über Bäume 1. Induktionsanfang mit Tiefe = 0 K = Knoten = 2. Induktionsannahme mit Tiefe = n, K= 3. Induktionsschritt Tiefe = n+1, K = +m n ⋅ m m n +1 − 1 + (m n +1 ) ⋅ (m − 1) = m −1 Prof. Dr. Margarita Esponda Funktionale Programmierung Strukturelle Induktion über Bäume Beispiel: data Tree a = Nil | Leaf a | Node (Tree a) (Tree a) Vorgehensweise: 1. Induktionsanfang 1.1 Wir zeigen eine bestimmte Eigenschaft P für den leeren Baum Nil 1.2 Wir zeigen die Eigenschaft P für ein Blatt (Leaf a) 2. Induktionsschritt Wir zeigen die Eigenschaft P für ( Node l r ) unter der Annahme, dass P für den Teilbaum l und für den Teilbaum r gilt. Prof. Dr. Margarita Esponda Funktionale Programmierung Das allgemeine Induktionsschema data T a1 a2 . . . am = C1 t11 … t1n1 | C2 t21 … t2n2 ... Vorgehensweise: | Ck tk1 … tknk 1. Induktionsanfang 1.1 Wir zeigen eine bestimmte Eigenschaft P für alle Basis-Daten (alle Ci ohne Rekursion) 2. Induktionsschritt Wir zeigen die Eigenschaft P für jede rekursive Definition (Ci ti1 … tini) unter der Annahme, dass P für ti1 … tini gilt. Prof. Dr. Margarita Esponda Funktionale Programmierung Analyse von Programmeigenschaften - Korrektheit (wichtigste Eigenschaft!) - Programmeigenschaften - wie z.B. Äquivalenz zwischen Programmen bzw. Ausdrücken - Komplexitätsaufwand - Speicherverbrauch - Terminierbarkeit Prof. Dr. Margarita Esponda Funktionale Programmierung Beispiele endrekursiver Funktionen Klassisches Beispiel einer nicht endrekursiven Definition ist: Die Standarddefinition der reverse-Funktion rev :: [a] -> [a] rev [] = [] rev (x:xs) = rev xs ++ [x] Berechnungsaufwand von rev: Reduktionen rev [x1, x2, …, xn] => rev [x2, …, xn] ++ [x1] => rev [x3, …, xn] ++ [x2] ++ [x1] 1 1 ... => [xn] ++ [xn-1] ++ [x2] ++ [x1] 1 => [] ++ [xn] ++ … ++ [x2] ++ [x1] 1 bis hier (n+1) Reduktionen! Prof. Dr. Margarita Esponda Funktionale Programmierung Berechnungsaufwand von rev bis hier (n+1) Reduktionen! (++) :: [a] -> [a] -> [a] (++) [] ys = ys (++) (x:xs) ys = x:(xs ++ ys) Reduktionen => [] ++ [xn] ++ [xn-1] ++ … ++ [x2] ++ [x1] => [xn] ++ [xn-1] ++ … ++ [x2] ++ [x1] 1 => [xn, xn-1] ++ … ++ [x2] ++ [x1] 2 => [xn, xn-1 ,xn-2] ++ … ++ [x2] ++ [x1] 3 => . . . . => [xn, xn-1 , … ,x1] n Die gesamte Anzahl der Reduktionen ist: Prof. Dr. Margarita Esponda Quadratischer Ausführungsaufwand! Funktionale Programmierung Eine effizientere Version von rev quickRev xs = rev_helper xs [] where rev_helper [] ys = ys rev_helper (x:xs) ys = rev_helper xs (x:ys) Berechnungsaufwand: Reduktionen quickRev [x1, x2, …, xn] => rev_helper [x1,…,xn] [] 1 => rev_helper [x2,…,xn] (x1:[]) 1 n => rev_helper [x3,…,xn] (x2:x1:[]) 1 ... => (xn:, … ,x2:x1:[]) … 1 => (xn:, … ,x2:[x1]) 1 ... => (xn:, … , x3:[x2,x1]) … 1 lineare Komplexität Prof. Dr. Margarita Esponda 2n = O(n) n Funktionale Programmierung Sind rev und quickRev äquivalent? Zu beweisen ist: Für alle endlichen Listen xs :: [a] gilt: rev = quickRev . . .Beweis an der Tafel . . . Prof. Dr. Margarita Esponda Funktionale Programmierung Wichtiges Beispiel von Endrekursion foldl-Funktion: foldl :: (b -> a -> b) -> b -> [a] -> b foldl f z [] = z foldl.1 foldl f z (x:xs) = foldl f (f z x) xs foldl.2 Hier werden Zwischenergebnisse akkumuliert und weitergeleitet. Mit Hilfe von Faltungs-Operatoren können sehr leicht endrekursive Funktionen definiert werden. Beispiel: reverse_reloaded = foldl (flip (:)) [] flip :: (a -> b -> c) -> b -> a -> c flip f x y Prof. Dr. Margarita Esponda = fyx Funktionale Programmierung Berechnungsverlauf reverse_reloaded [x1, x2, … , xn] foldl.2 => foldl (flip (:)) [] [x1, x2,…, xn] => foldl (flip (:)) ((flip (:)) [] x1) [x2,x3,…, xn] => foldl (flip (:)) ((:) x1 []) [x2,x3,…, xn] => foldl (flip (:)) (x1:[]) [x2,x3,…, xn] => foldl (flip (:)) [x1] [x2,x3,…, xn] foldl.2 => foldl (flip (:)) ((flip (:)) [x1] x2) [x3,…, xn] => foldl (flip (:)) ((:) x2 [x1]) [x3,…, xn] => foldl (flip (:)) (x2:[x1]) [x3,…, xn] foldl.2 Prof. Dr. Margarita Esponda => foldl (flip (:)) [x2, x1] [x3,…, xn] => . . . Berechnung der Fibonacci-Zahlen 1. Lösung fib 0 = 0 fib.0 fib 1 = 1 fib.1 fib n = fib (n-2) + fib (n-1) fib.2 2. Lösung Endrekursive Funktion fib' n = quickFib 0 1 n where quickFib a b 0 = a quickFib.1 quickFib a b n = quickFib b (a+b) (n-1) quickFib.2 Funktionale Programmierung Sind fib und fib' äquivalent? Zu beweisen ist: fib = fib' Für unseren Beweis müssen wir folgende Eigenschaft der quickFibFunktion per Induktion über n zeigen. quickFib (fib i) (fib (i+1)) n = fib (i+n) ……… ze.1 Induktionsanfang: für n = 0 quickFib (fib i) (fib (i+1)) 0 = fib i = fib (i+0) quickFib.1 Prof. Dr. Margarita Esponda Funktionale Programmierung Induktions-Annahme: quickFib (fib i) (fib (i+1)) n = fib (i+n) Induktionsschritt: ? quickFib (fib i) (fib (i+1)) (n+1) = fib (i+(n+1)) quickFib (fib i) (fib (i+1)) (n+1) quickFib.2 fib.2 Induktions-Annahme = quickFib fib (i+1) (fib (i) + fib (i+1)) n = quickFib fib (i+1) (fib ((i+1)+1) n = fib ((i+1) + n) = fib (i + (n+1)) Prof. Dr. Margarita Esponda Funktionale Programmierung Sind fib und fib' äquivalent? Behauptung: fib = fib' 1. Induktionsanfang: fib 0 = 0 = quickFib 0 1 0 = fib' 0 quickFib.1 2. Induktionsanfang: fib 1 = 1 fib' 1 = quickFib 0 1 1 fib'.1 = quickFib 1 1 0 quickFib.2 = 1 quickFib.1 Prof. Dr. Margarita Esponda Funktionale Programmierung Induktionsannahme: fib n = fib' n Induktionsschritt: ? fib (n+1) = fib' (n+1) fib' (n+1) = quickFib 0 1 (n+1) fib'.1 = quickFib (fib 0) (fib 1) (n+1) fib.0 und fib.1 = fib ((n+1)+0) aus ze.1 = fib (n+1) Prof. Dr. Margarita Esponda