Grundlagen der Programmierung 2 (1.B) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 26. April 2006 Aufrufhierarchie und Rekursive Definitionen f, g, fi seien Haskell-definierte Funktionen. f referenziert g direkt, f referenziert g (indirekt), f ist direkt rekursiv, f ist rekursiv, Verschränkte Rekursion: Grundlagen der Programmierung 2 (1.B) wenn g im Rumpf von f vorkommt. wenn es Funktionen f1, . . . , fn gibt, so dass gilt: f referenziert direkt f1, f1 referenziert direkt f2, . . . , fn referenziert direkt g. wenn f sich selbst direkt referenziert. wenn f sich selbst (indirekt) referenziert. wenn f die Funktion g referenziert und g die Funktion f auch für allgemeinere Fälle - 1 - Beispiel: Aufrufhierarchie quadrat x = x*x quadratsumme x y = (quadrat x) + (quadrat y) quadratsumme ruft direkt die Funktion quadrat auf, quadratsumme ruft direkt die (eingebaute) Funktion ∗ auf Die Funktion quadratsumme ist somit nicht rekursiv Grundlagen der Programmierung 2 (1.B) - 2 - Beispiel: Fakultät 0! := 1 n! := n ∗ (n − 1)! n! ist die Anzahl aller Permutationen einer n-elementigen Menge. rekursive Definition: fakultaet:: Integer -> Integer fakultaet x = if x <= 0 then 1 else x*(fakultaet (x-1)) Diese Funktion ist rekursiv, da sie im Rumpf sich selbst wieder aufruft. Grundlagen der Programmierung 2 (1.B) - 3 - Entwurf rekursiver Funktionen Wichtig: zwei Fälle sind zu beachten der Basisfall: der Rekursionsfall: Ergebnis: 0 wenn das Argument x ≤ 1 ist. Ergebnis: x*(fakultaet (x-1)), wenn x > 1 ist. Terminierung bei rekursiven Aufrufen : • Argumente werden mit jedem rekursiven Aufruf kleiner fakultaet x ruft fakultaet (x-1) auf für x ≥ 1. • Der Basisfall hat das kleinste Argument Es funktioniert: Main> fakultaet 3 6 Main> fakultaet 40 815915283247897734345611269596115894272000000000 Grundlagen der Programmierung 2 (1.B) - 4 - Eine falsche Definition fakultaet_nt:: Integer -> Integer fakultaet_nt x = if x == 0 then 1 else x*(fakultaet_nt (x-1)) Diese Funktion terminiert nicht bei negativen Eingaben: fakultaet_nt (-5) ruft fakultaet_nt (-6) auf usw. Grundlagen der Programmierung 2 (1.B) - 5 - Definitionseinsetzung (δ-Reduktion) Auswerten einer Anwendung von f auf n Argumente: (f t1 . . . tn) → (Rumpff [t1/x1, . . . tn/xn]) wobei Die Definition von f ist: f x1 . . . xn = Rumpf f Grundlagen der Programmierung 2 (1.B) - 6 - Definitionseinsetzung f t1 . . . tn → (Rumpff [t1/x1, . . . tn/xn]) entsteht durch (paralleles bzw. unabhängiges) Ersetzen von xi durch ti. ti kann ein Ausdruck sein; es muss kein Basiswert sein! Grundlagen der Programmierung 2 (1.B) - 7 - Arithmetische Auswertungen v op w → r wenn: v, w arithmetische Basiswerte, op ein zweistelliger arithmetischer Operator, r Resultat von v op w op v → r wenn: v arithmetischer Basiswert, op einstelliger arithmetischer Operator, r Resultat von op v Beachte: Wenn s op t ausgewertet werden soll, dann zuerst die Ausdrücke s, t auswerten. Grundlagen der Programmierung 2 (1.B) - 8 - Beispiel: Programm: main = quadrat 5 quadrat x = x*x Auswertung als Folge von Transformationen: main → quadrat 5 → 5 * 5 → 25 Grundlagen der Programmierung 2 (1.B) - 9 - Boolesche Auswertungen v op w oder op w wobei op einer der Operatoren &&, ||, not sein kann. Wird als Anwendung des Operators auf Argumente ausgewertet. Definitionen, in der Wirkung äquivalent zu den vordefinierten: x && y = if x then y else False x || y = if x then True else y not x = if x then False else True Grundlagen der Programmierung 2 (1.B) - 10 - Auswertung der Fallunterscheidung Fallunterscheidung (if-Reduktion) (if True then e1 else e2) → e1 (if False then e1 else e2) → e2 Beachte Diese zwei Regeln können nur angewendet werden, wenn der Bedingungsausdruck zu True oder False ausgewertet ist. Grundlagen der Programmierung 2 (1.B) - 11 - Transformationen, Reduktionen Wir nennen eine Transformation auch Reduktion und eine Folge von Programmtransformationen auch Reduktionsfolge (oder Auswertung). Beachte: Reduktionen / Transformationen sind zunächst überall im Ausdruck erlaubt. Erst eine Auswertungs-Strategie macht die Auswertung eindeutig. Motivation für diese Transformationssemantik: • eindeutige Festlegung der Auswertung • Programmtransformationen, • Umgang mit nichtterminierenden Argumenten • Unendliche Listen Grundlagen der Programmierung 2 (1.B) - 12 - Beispiel x && y = if x then y x || y = if x then True bot = bot else False else y Auswertungen unter der Annahme der obigen Definition True && True → if True then True else False → True True && False → if True then False else False → False True || True → if True then True else True → True True || False → if True then True else False → True True && bot → if True then bot else False → bot → bot . . . True || bot → if True then True else bot → True Grundlagen der Programmierung 2 (1.B) - 13 - Transformationsmöglichkeiten 3 verschiedene Auswertungen für quadrat (4+5) : 1. quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → 9 ∗ (4 + 5) → 9 ∗ 9 → 81 2. quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → (4 + 5) ∗ 9 → 9 ∗ 9 → 81 3. quadrat(4 + 5) → (quadrat 9) → 9 ∗ 9 → 81 Beobachtungen: • • Ergebnis ist gleich Anzahl der Reduktionen verschieden Grundlagen der Programmierung 2 (1.B) - 14 - Reduktionsstrategien Zwei wichtige Reduktionsstrategien Applikative Reihenfolge: Argumentauswertung vor δ-Reduktion Normale Reihenfolge: δ-Reduktion vor Argumentauswertung Grundlagen der Programmierung 2 (1.B) - 15 - Beschreibung: applikative Reihenfolge werte t0 applikativ aus! Fälle: • • • • t0 ist Basiswert. fertig. t0 ≡ s t, Wenn s kein Funktionsname und keine Anwendung, dann applikativ s t0 ≡ f t1 . . . tn. Wenn ar(f ) ≤ n, dann applikativ ti, 1 ≤ i ≤ ar(f ) von links nach rechts. Wenn ar(f ) ≤ n und alle ti Basiswerte, dann δ-Reduktion. Wenn n < ar(f ), dann fertig: keine Reduktion. t0 ≡ if b then e1 else e2. Wenn b Basiswert, dann if-Reduktion Wenn b kein Basiswert, dann applikativ b Grundlagen der Programmierung 2 (1.B) - 16 - Beschreibung: normale Reihenfolge werte t0 in normaler Reihenfolge aus. Fälle: • t0 ist Basiswert. fertig. • t0 ≡ s t, Wenn s kein Funktionsname und keine Anwendung. Dann normale R. auf s • t0 ≡ f t1 . . . tn und f keine eingebaute Funktion, Wenn ar(f ) ≤ n, dann δ-Reduktion auf f t1 . . . tar(f ). Wenn ar(f ) > n: keine Reduktion. • t0 ≡ f t1 . . . tn und f ist eingebaute Funktion Wenn ar(f ) ≤ n und Argumente von f keine Basiswerte, dann normale R. auf ar(f ) Argumente von links nach rechts. Wenn ar(f ) ≤ n, und ar(f ) Argumente von f sind Basiswerte, dann eingebaute Funktion aufrufen. Wenn ar(f ) > n: keine Reduktion. • t0 ≡ if b then e1 else e2. Wenn b Basiswert, dann if-Reduktion Wenn b kein Basiswert, dann normale R. auf b Grundlagen der Programmierung 2 (1.B) - 17 - Beispiel für verschiedene Reduktionen 3 Auswertungen für quadrat (4+5) : 1. quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → 9 ∗ (4 + 5) → 9 ∗ 9 → 81 normale Reihenfolge der Auswertung 2. quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → (4 + 5) ∗ 9 → 9 ∗ 9 → 81 3. quadrat(4 + 5) → (quadrat 9) → 9 ∗ 9 → 81 applikative Reihenfolge der Auswertung Grundlagen der Programmierung 2 (1.B) - 18 - Beispiel: Auswertung main = fakultaet 4 fakultaet x = if x <= 1 then 1 else x*(fakultaet (x-1)) Folge von Transformationen: (Definitionseinsetzung) fakultaet 4 if 4 <= 1 then 1 else 4*(fakultaet (4-1)) (Auswertung arithmetische Ungleichung) if False then 1 else 4*(fakultaet (4-1)) (if-Auswertung) Grundlagen der Programmierung 2 (1.B) - 19 - Beispiel: normale Reihenfolge der Auswertung (2) 4*(fakultaet (4-1)) (zweites Argument von * wird per Transformation ausgewertet) 4*(if (4-1) <= 1 then 1 else (4-1)*(fakultaet ((4-1)-1))) 4*(if 3 <= 1 then 1 else (4-1)*(fakultaet ((4-1)-1))) 4*(if False then 1 else (4-1)*(fakultaet ((4-1)-1))) 4*((4-1)*(fakultaet ((4-1)-1))) 4*(3*(fakultaet ((4-1)-1))) 4*(3*( if ((4-1)-1) <= 1} Grundlagen der Programmierung 2 (1.B) then 1 else ((4-1)*(fakultaet (((4-1)-1)-1)))) - 20 - Beispiel: normale Reihenfolge der Auswertung (3) 4*(3*(if (3-1) <= 1 then 1 else ((4-1)-1)*(fakultaet (((4-1)-1)-1)))) 4*(3*(if 2 <= 1 then 1 else ((4-1)-1)*(fakultaet ((4-1)-1)-1)))) 4*(3*(if False then 1 else ((4-1)-1)*(fakultaet (((4-1)-1)-1)))) 4*(3*(((4-1)-1) *(fakultaet (((4-1)-1)-1)))) 4*(3*(((3-1) *(fakultaet (((4-1)-1)-1)))) 4*(3*(2*(fakultaet (((4-1)-1)-1)))) 4*(3*(2*(if (((4-1)-1)-1) <= 1 then 1 else (((4-1)-1)-1)*(fakultaet (((4-1)-1)-1)-1)))) Grundlagen der Programmierung 2 (1.B) - 21 - Beispiel: normale Reihenfolge der Auswertung (4) 4*(3*(2*( if ((3-1)-1) <= 1 then 1 else (((4-1)-1)-1)*fakultaet ((((4-1)-1)-1)-1))))) 4*(3*(2*( if (2-1) <= 1 then 1 else (((4-1)-1)-1)*fakultaet ((((4-1)-1)-1)-1))))) 4*(3*(2*( if 1 <= 1 then 1 else (((4-1)-1)-1)*fakultaet ((((4-1)-1)-1)-1))))) 4*(3*(2*( if True then 1 else (((4-1)-1)-1)*fakultaet ((((4-1)-1)-1)-1)))) Grundlagen der Programmierung 2 (1.B) - 22 - Beispiel: normale Reihenfolge der Auswertung (5) 4*(3*(2*1) ) 4*(3*2) 4*6 24 (24 Auswertungsschritte) Grundlagen der Programmierung 2 (1.B) - 23 - Beispiel: applikative Reihenfolge der Auswertung fakultaet 4 if 4 <= 1 then 1 else 4*(fakultaet (4-1)) if False then 1 else 4*(fakultaet (4-1)) 4*(fakultaet (4-1) ) (wegen applikativer Reihenfolge wird zuerst das Argument von falkultaet ausgewertet) 4*(fakultaet 3 ) 4*(if 3 <= 1 then 1 Grundlagen der Programmierung 2 (1.B) else 3*(fakultaet (3-1))) - 24 - Beispiel: applikative Reihenfolge der Auswertung (2) 4*(if False then 1 else 3*(fakultaet (3-1))) 4*(3*(fakultaet (3-1) )) 4*(3*(fakultaet 2) ) 4*(3*(if 2 <= 1 then 1 else 2*(fakultaet (2-1)))) 4*(3*(if False then 1 else 2*(fakultaet (2-1)))) 4*(3*(2*(fakultaet (2-1) ))) 4*(3*(2*(fakultaet 1) ) 4*(3*(2*(if 1 <= 1 then 1 Grundlagen der Programmierung 2 (1.B) else 1*(fakultaet (1-1))))) - 25 - Beispiel: applikative Reihenfolge der Auswertung (3) 4*(3*(2*(if True then 1 else 1*(fakultaet (1-1))))) 4*(3*(2*1) ) 4*(3*2) 4*6 24 18 Auswertungsschritte Grundlagen der Programmierung 2 (1.B) - 26 - Optimale Anzahl der Reduktionen Definition verzögerte Reihenfolge der Auswertung (lazy reduction): • • • normale Reihenfolge gerichteter Graph statt Text Vermeidung von unnötiger Doppelauswertung durch gemeinsame Unterausdrücke (Sharing) Falls ein Basiswert berechnet wird, gilt für #Reduktionsschritte: # verzögerte R ≤ # applikative R ≤ # normale R Es gilt: verzögerte Reduktion hat optimale Anzahl von Reduktionen Haskell verwendet verzögerte Reduktion Grundlagen der Programmierung 2 (1.B) - 27 - Beispiel 1. 4 Reduktionen: (normale R.) quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → 9 ∗ (4 + 5) → 9 ∗ 9 → 81 2. 4 Reduktionen: quadrat(4 + 5) → (4 + 5) ∗ (4 + 5) → (4 + 5) ∗ 9 → 9 ∗ 9 → 81 3. 3 Reduktionen (applikative R.) quadrat(4 + 5) → (quadrat 9) → 9 ∗ 9 → 81 4. 3 Reduktionen (verzögerte R.) quadrat(4 + 5) → (4 + 5)(1) ∗ (4 + 5)(1) → 9 ∗ 9 → 81 Grundlagen der Programmierung 2 (1.B) - 28 - Verzögerte Auswertung: komplexeres Beispiel fakultaet x = if x <= 1 then 1 else x*(fakultaet (x-1)) Die Reduktionsschritte: fakultaet 4 if 4 <= 1 then 1 else 4*(fakultaet (4-1)) if False then 1 else 4*(fakultaet (4-1)) 4*(fakultaet (4-1)) 4*(if (4-1) <= 1 then 1 4*(if 3 <= 1 then 1 Grundlagen der Programmierung 2 (1.B) else (4-1)*(fakultaet ((4-1)-1))) else 3*(fakultaet (3-1))) - 29 - komplexeres Beispiel . . . 4* (if False then 1 else 3*(fakultaet (3-1))) 4*(3*(fakultaet (3-1))) 4*(3*(if (3-1) <= 1 then 1 else (3-1)*(fakultaet ((3-1)-1)))) 4*(3*(if 2 <= 1 then 1 else 2*(fakultaet (2-1)))) 4*(3*(if False then 1 else 2*(fakultaet (2-1)))) 4*(3*(2*(fakultaet (2-1)) )) 4*(3*(2*( if (2-1) <= 1 then 1 Grundlagen der Programmierung 2 (1.B) else (2-1)*(fakultaet ((2-1)-1))))) - 30 - komplexeres Beispiel . . . 4*(3*(2*( if 1 <= 1 then 1 else 1*(fakultaet (1-1))))) 4*(3*(2*(if True then 1 else 1*(fakultaet (1-1))))) 4*(3*(2*1)) 4* (3*2) 4*6 24 (18 Auswertungsschritte) Grundlagen der Programmierung 2 (1.B) - 31 - Ein weiterer Vorteil der verzögerten Reduktion verzögerte Reduktionenen zur Compile-Zeit sind korrekte Programmtransformationen d.h. die operationale Semantik bleibt erhalten Das ist falsch bei applikativer Reihenfolge Grundlagen der Programmierung 2 (1.B) - 32 - Rekursive Auswertungsprozesse in Haskell Auswertungsprozess, der durch eine rekursive Funktion bewirkt wird Wir betrachten bei Auswertungsprozessen nur die applikative Reihenfolge Beispiel: Auswertung der rekursiven Fakultätsfunktion 0! := 1 n! := n ∗ (n − 1)! fakultaet x = if x <= 1 then 1 else x*(fakultaet (x-1)) Grundlagen der Programmierung 2 (1.B) - 33 - Auswertungsprozess, linear rekursiv bei applikativer Reihenfolge der Auswertung (Nicht jeder Zwischenzustand ist angegeben) (fakultaet 6) (6 * (fakultaet (6-1))) (6 * (5 * (fakultaet (5-1)))) (6 * (5 * (4 * (fakultaet (4-1))))) (6 * (5 * (4 * (3 * (fakultaet (3-1)))))) (6 * (5 * (4 * (3 * (2 * (fakultaet (2-1))))))) (6 * (5 * (4 * (3 * (2 * 1))))) (6 * (5 * (4 * (3 * 2)))) (6 * (5 * (4 * 6))) (6 * (5 * 24)) (6 * 120) 720 Grundlagen der Programmierung 2 (1.B) - 34 - Auswertungsprozess, linear rekursiv (fakultaet 6) Auswertungsprozess ist linear rekursiv Charakteristisch: nur eine rekursive Funktionsanwendung in jedem Ausdruck der Reduktionsfolge Zwischenausdrücke sind unbeschränkt in der Größe Grundlagen der Programmierung 2 (1.B) - 35 - Alternative Berechnungen der Fakultätsfunktion Iteriere folgende Regel: Produkt Zähler ⇒ ⇒ Produkt ∗ Zähler Zähler + 1 Programm: fakultaet_iter n = fakt_iter 1 1 n fakt_iter produkt zaehler max = if zaehler > max then produkt else fakt_iter (zaehler * produkt) fakultaet_lin n = fakt_iter Grundlagen der Programmierung 2 (1.B) (zaehler + 1) max 1 1 n - 36 - Endrekursion Eine Endrekursion ist eine lineare Rekursion. Zusätzlich muss gelten: alle rekursiven Aufrufe berechnen den Rückgabewert ohne Nachverarbeitung Grundlagen der Programmierung 2 (1.B) - 37 - Auswertungsprozess, endrekursiv Auswertung von (fakultaet_iter 5) bei verzögerter Reihenfolge der Auswertung (fakultaet_iter 5) (fakt_iter 1 1 5) (fakt_iter (1*1) (1+1) 5) (fakt_iter (2*(1*1)) (2+1) 5) (fakt_iter (3*(2*(1*1))) (3+1) 5) (fakt_iter (4*(3*(2*(1*1)))) (4+1) 5) (fakt_iter (5*(4*(3*(2*(1*1))))) (5+1) 5) (5*(4*(3*(2*(1*1))))) 120 Das ist eine lineare Rekursion, es ist auch eine Endrekursion Auswertungsprozess zu fakultaet 6 nicht endrekursiv, da fakultaet (...) eingebettet in weitere Berechnung. Grundlagen der Programmierung 2 (1.B) - 38 - Iterativer Auswertungsprozess: Fakultät (2) Iterativer Auswertungsprozess bei applikativer Auswertung: (fakultaet_iter 6) (fakt_iter 1 1 6) (fakt_iter 1 2 6) (fakt_iter 2 3 6) (fakt_iter 6 4 6) (fakt_iter 24 5 6) (fakt_iter 120 6 6) (fakt_iter 720 7 6) 720 Iterativer Prozess: Charakteristisch: Ist eine Endrekursion Argumente sind Basiswerte (bzw. Größe des Gesamtausdrucks bleibt beschränkt.) optimierte Rückgabe des Wertes Grundlagen der Programmierung 2 (1.B) - 39 - Optimierung der Endrekursion imperative Programmiersprachen Endrekursion i.a. nicht optimiert. d.h. Wert wird durch alle Stufen der Rekursion zurückgegeben Endrekursion ist optimiert am Ende wird Wert unmittelbar zurückgegeben. Haskell Deshalb braucht man in imperativen Programmiersprachen: Iterationskonstrukte for ...do, while, Grundlagen der Programmierung 2 (1.B) repeat ...until. - 40 - Iteration in Haskell In Haskell: Iterative Auswertungsprozesse sind auch bei verzögerter Auswertung entsprechende Programmierung ist erforderlich Naive Implementierung, verzögerte Reihenfolge der Auswertung tendieren zu: linear rekursiven, nicht endrekursiven bzw. nicht iterativen Prozessen. Vergleich: Iterativ gegen linear rekursiv: Anzahl Auswertungsschritte bleibt i.a. gleich Größe der Zwischenausdrücke ist kleiner Grundlagen der Programmierung 2 (1.B) - 41 - Baumrekursion Beispiel Berechnung der Fibonacci-Zahlen 1, 1, 2, 3, 5, 8, 13, 21, . . . F ib(n) := 0 1 F ib(n − 1) + F ib(n − 2) falls n = 0 falls n = 1 sonst fib n = if n <= 0 then 0 else if n == 1 then 1 else fib (n-1) + fib(n-2) Grundlagen der Programmierung 2 (1.B) - 42 - Auswertungsprozess zu fib in applikativer Reihenfolge: Der Auswertungs-Prozess ergibt folgende Zwischen-Ausdrücke: fib 5 fib 4 + fib 3 (fib 3 + fib 2) + fib 3 ((fib 2 + fib 1) + fib 2) + (((fib 1 + fib 0) + fib 1) + (((1+0) + fib 1) + fib 2) + ((1 + fib 1) + fib 2) + fib ((1+1) + fib 2) + fib 3 (2 + fib 2) + fib 3 (2 + (fib 1 + fib 0)) + fib ....... Grundlagen der Programmierung 2 (1.B) fib 3 fib 2) fib 3 3 + fib 3 3 - 43 - Auswertungsprozess zu fib: Baum der Aufrufe 5 4 3 3 2 1 2 1 1 2 0 1 1 0 0 Das ist Baumrekursion Charakteristisch: Ausdrücke in der Reduktionsfolge • können unbegrenzt wachsen • enthalten mehrere rekursive Aufrufe • Aber: nicht geschachtelt (d.h. die Argumente eines rekursiven Aufrufs enthalten keine rekursiven Aufrufe) Grundlagen der Programmierung 2 (1.B) - 44 - Geschachtelte Rekursion Ist der allgemeine Fall wird normalerweise selten benötigt,da i.a. nicht effizient berechenbar. Beispiel: Die Ackermannfunktion ----ack 0 ack 1 ack x ack x Ackermanns Funktion y = 0 = 0 | x >= 2 = y | x > 0 && y > 0 = ---1 2 x+2 ack (ack (x-1) y) (y-1) Vorstellung neuer syntaktischer Möglichkeiten in Haskell: Auswertung: von oben nach unten wird probiert, welche Definitionsgleichung passt: 1) Argumente anpassen 2) Bedingung rechts vom | prüfen Grundlagen der Programmierung 2 (1.B) - 45 - Optimierte Ackermannfunktion ----ackopt ackopt ackopt ackopt ackopt ackopt Ackermanns Funktion optimiert ---0 y = 1 1 0 = 2 x 0 = x+2 x 1 = 2*x x 2 = 2^x x y | x > 0 && y > 0 = ackopt (ackopt (x-1) y) (y-1) *Main> logI10 (ackopt 5 3) 19728.301029995662 • • • --(Anzahl DezimalStellen) -- ( == 2^65536) sehr schnell wachsende Funktion man kann nachweisen: ack nicht primitiv rekursiv hat Anwendung in der Komplexitätstheorie Grundlagen der Programmierung 2 (1.B) - 46 - Tabelle der Rekursionsprozesse: linear rekursiv endrekursiv iterativ Baumrekursion geschachtelte Baumrekursion maximal ein rekursiver Unterausdruck linear rekursiv und Gesamtresultat ist Wert des rekursiven Unterausdrucks endrekursiv und Argumente des rekursiven Unterausdrucks sind Basiswerte mehrere rekursive Unterausdrücke; Argument der rekursiven Unterausdrücke ohne weitere Rekursion mehrere rekursive Unterausdrücke auch in den Argumenten Grundlagen der Programmierung 2 (1.B) - 47 - Optimierung: Iteration statt Rekursion Beispiel Berechnung von (fib fib 3 wird fib 2 wird fib 1 wird 5) 2 mal berechnet 3 mal berechnet 5 mal berechnet Genauer: Bei Berechnung von fib n für n ≥ 2 wird fib(1) jeweils (fib n)-mal berechnet Φn fib n ≈ √ Fazit: 5 √ wobei Φ = 1+2 5 ≈ 1.6180 fib(n) wächst exponentiell # Reduktionen für fib(n) ist exponentiell d.h. die Laufzeit von fib ist exponentiell Grundlagen der Programmierung 2 (1.B) - 48 - Iterative Version von Fib Beobachtung: zur Berechnung von fib(n) benötigt man nur die Werte fib(i) für 1 ≤ i ≤ n. Idee: Berechnung einer Wertetabelle für fib. Verbesserte Variante: aus fib (n − 1) und fib (n − 2) berechne fib(n) Ohne Doppelberechnung Rechenvorschrift: (a, b) → (a + b, a) Grundlagen der Programmierung 2 (1.B) - 49 - Iterative Version von fib: Funktionen fib_lin n = fib_iter a b zaehler = Grundlagen der Programmierung 2 (1.B) (fib_iter 1 0 n) if zaehler <= 0 then b else fib_iter (a + b) a (zaehler - 1) - 50 - Prozess für (fib lin 5) (fib_lin 5) (fib_iter (fib_iter (fib_iter (fib_iter (fib_iter 5 1 1 2 3 5 0 1 1 2 3 5) 4) 3) 2) 1) Grundlagen der Programmierung 2 (1.B) - 51 - Analyse der fib-Optimierung Für (fib_lin n) gilt: • ist operational äquivalent zu fib • benötigt linear viele Reduktionen abhängig von n • Größe der Ausdrücke ist beschränkt • Platzbedarf ist konstant (d.h. unabhängig) von n. (unter Vernachlässigung der Darstellungsgröße der Zahlen) erzeugt Iterativen Auswertungsprozess Grundlagen der Programmierung 2 (1.B) - 52 - Analyse von Programmen Wir verwenden für Haskell-Programme folgende Messungen. Bei verzögerter Auswertung Zeit: Anzahl der Transformationsschritte Platz: (Gesamt-Speicher): Maximale Größe der Ausdrücke Arbeitsspeicher: Maximale Größe der Ausdrücke (ohne die Eingabe) arithmetische und Boolesche Operationen = 1 Transformationsschritt. Grundlagen der Programmierung 2 (1.B) - 53 - Analyse von Programmen (2) Angabe des Ressourcenbedarf eines Algorithmus in Abhängigkeit von der Größe der Eingabe. Notation für Algorithmus alg bei Eingabe der Größe n: redalg(n) maximale Anzahl der Reduktionen bei verzögerter Auswertung für alle Eingaben der Größe n. P latzalg(n) Platzbearf: maximale Größe der Ausdrücke (des gerichteten Graphen) bei verzögerter Auswertung für alle Eingaben der Größe n. Die Eingaben nicht mitzählen Grundlagen der Programmierung 2 (1.B) - 54 - Beispiel: fib fib n = if n <= 0 then 0 else if n == 1 then 1 else fib (n-1) + fib(n-2) redfib(n) ≈ 1.6n (einfach exponentiell) andere Bezugsgröße: Größe der Darstellung der Zahl n: size(n) = dlog10(n)e Dezimalstellen. n hat size(n) ) (10 Zeitbedarf: ≈ 1.6 (doppelt exponentiell). Grundlagen der Programmierung 2 (1.B) - 55 - Beispiel fakultaet n fakultaet n benötigt 5 ∗ (n − 1) + 3 Reduktionsschritte. Nachweis mit vollständiger Induktion: Grundlagen der Programmierung 2 (1.B) - 56 - Komplexitäten von Algorithmen Beachte: breite Streuung des Ressourcenbedarfs ist möglich für die Menge aller Eingaben einer bestimmten Größe. Komplexitäten von Platz und Zeit: Ressourcenbedarf im schlimmsten Fall im besten Fall im Mittel Grundlagen der Programmierung 2 (1.B) (worst-case) (best-case) Minimum von # Reduktionen bzw. Minimum der Größe der Ausdrücke. (average case) Welche Verteilung? - 57 - Einige Komplexitäten O(1) O(log(n)) O(n) O(n ∗ log(n)) O(n2) O(n3) O(nk ) O(2n) konstant logarithmisch linear fastlinear (oder auch n-log-n) quadratisch kubisch polynomiell exponentiell Grundlagen der Programmierung 2 (1.B) - 58 - Beispiel fib fib Bezugsgröße: n: Eingabe d: Größe der Darstellung der Zahl n redfib(n) platzfib(n) = = O(1.62n) O(n) redfib lin(n) platzfib lin(n) = = O(n) O(n) redfib lin(d) platzfib lin(d) = = O(10d) O(10d) Grundlagen der Programmierung 2 (1.B) - 59 - Analyse des Algorithmus zum größten gemeinsamen Teiler ggT(a, b) (Euklids Algorithmus) Teile a durch b gibt Rest r, wenn r = 0, dann ggT(a, b) := b wenn r 6= 0, dann berechne ggT(b, r). Beispiel ggT(30, 12) = ggT(12, 6) = 6 ggt a b = if b == 0 then a else ggt b (rem a b) Grundlagen der Programmierung 2 (1.B) - 60 - Satz von Lamé Komplexität des Euklidischen Algorithmus SATZ (Lamé): benötigt, Wenn der Euklidische ggt-Algorithmus k Schritte dann ist die kleinere Zahl der Eingabe ≥ f ib(k). Platz- und Zeitbedarf von ggt: O(log(n)) Begründung: Wenn n die kleinere Zahl ist und der Algorithmus k Schritte benötigt, dann ist n ≥ f ib(k) ≈ 1.6180k Also ist k = O(log(n)) Grundlagen der Programmierung 2 (1.B) - 61 - Listen und Listenfunktionen Listen modellieren Folgen von gleichartigen, gleichgetypten Objekten. [0,1,2,3,4,5,6,7,8,9] Typ: [Integer]; d.h. Liste von Integer. [] leere Liste, (Nil) [’a’, ’b’, ’c’] Typ: [Char]; abgekürzt als String Druckbild: "abc" [[], [0], [1, 2]] Liste von Listen; Typ [[Integer]], d.h. eine Liste von Listen von Integer-Objekten. [1..] potentiell unendliche Liste [1,2,3,...] Grundlagen der Programmierung 2 (1.B) - 62 - Listen und Listenfunktionen zwei Schreibweisen für Listen: [0, 1, 2] schöne Darstellung Druckbild einer Liste (0 : (1 : (2 : []))) interne Darstellung mit zweistelligem Infix-Listen-Konstruktor : “ ” und dem Konstruktor [] Eingebaute, listenerzeugende Funktionen: [n..] [n..m] [1..10] [n,m..k] erzeugt die Liste der Zahlen ab n. erzeugt die Liste von n bis m ergibt [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] erzeugt die Liste von n bis k mit Schritten m − n Grundlagen der Programmierung 2 (1.B) - 63 - Darstellung von Listen Listen sind aufgebaut mittels zwei Konstruktoren: [] : Konstante für die leere Liste Zweistelliger Infix-Konstruktor Linkes Argument: erstes Element der Liste Rechtes Argument: Restliste Beispiel für Haskells Listenerzeugung: 8:[] 9:(8:[]) 10:(9:(8:[])) Liste [8] mit dem Element 8 Liste [9.8] mit zwei Elementen 8,9 Liste [10,9,8] mit drei Elementen Grundlagen der Programmierung 2 (1.B) - 64 - Baum-Bild einer Liste zz zz z zz z} z : @@@ 10 @@ @@ @@ 9 : <<< << << < 8 Grundlagen der Programmierung 2 (1.B) : ;;; ;; ;; ; [] - 65 - Einfache Listenfunktionen Definitionen head (x:xs) tail (x:xs) = x = xs --Erstes Element --Restliste Auswertungen head [] head [1] tail [] tail [1] Grundlagen der Programmierung 2 (1.B) - 66 - Beispiel lengthr lengthr [] = 0 lengthr (x:xs) = 1 + (lengthr xs) Auswertung lengthr (10:(9:(8:[]))) 1+ (lengthr (9:(8:[]))) 1+(1+ (lengthr (8:[]))) 1+(1+ (1+ (lengthr []))) 1+(1+ (1+ (0))) 3 Grundlagen der Programmierung 2 (1.B) Zweiter Fall; [10/x,(9:(8:[]))/xs] Zweiter Fall; [9/x, (8:[])/xs] Zweiter Fall; [8/x, ([])/xs] Erster Fall; 3 × Addition - 67 -