Terminierung funktionaler Programme Obwohl für viele Rekursive Vorschrifften offensichtlich, kann diese für viele Funktionen nicht unmittelbar abgelesen werden (z.B.: obiges Programm zur Permutation, Ackermannfunktion). Deshalb wird Formalismus benötigt. Wir betrachten Rechenvorschrifften der Funktionalität fct f = (m x)n :E Sei M die Trägermenge der Sorte m und M- = M \{ }. Hier verstanden als Ergebniss einer nicht terminierenden Funktion Dann terminiert die Funktion f xM, wenn xM: f(x) ≠ 07.05.03 Um die Terminierungseigenschaft einer Fkt. zu zeigen, verwendet man eine sog. „Abstiegsfunktion“ h: M- IN0; h gibt eine Abschätzung für die Anzahl rekursiver Aufrufe bei linearer Rekursion bzw. der Höhe des Aufrufbaums bei nichtlinearer Rekursion. Zum Beweis der Terminierung muss folgendes Prädikat gezeigt werden: P [k IN 0 , x M , h( x) k f ( x) ] Es muss also für alle k gezeigt werden, dass P gilt, bzw. Berechnung der Funktion f umfasst max. k Schritte für Argument x. Nachweis durch Induktion: Für den IA (k=0) ist zu zeigen: x M , wenn h(x) = 0 => f ( x) , d.h. f(x) terminiert. IS: # Ind.annahme: P gilt für alle k’ k # z.z ist dann, dass aus (h( x) k 1) die Gültigkeit von f ( x) folgt. 1. Beispiel: Terminierung der FKT ggT(a,b) (mit a und b größer gleich 1) let rec ggT(a,b) = match (a,b) with |(a,b) when a= b a |(a,b) when a<b ggT(a,b-a) |(a,b) when a>b ggT(a-b,b);; 1.Schritt: Definition einer Ableitungsfunktion h : INxIN IN0 für a=b (Erzwinge ‘0’ für Terminierungsfall) 0 h( a, b) für (a>b) v (b>a) a b Es geht in diesem Schritt immer darum eine Fkt. zu finden, die in Abhängigkeit der Argumente des Fkt.aufrufes monoton fällt, und beim Terminierungsfall 0 landen wird. Induktionsanfang: h(a,b) = 0 => a = b => ggT(a,b) = a Induktionsschritt: - Induktionsannahme: a, b Sei also : : h(a, b) k ggT (a, b) 0 h( a, b) k 1 h(a, b) a b k 1 (a k ) (b k ) weil a 1 b 1 Fall I (a b) ggT (a, b) ggT (a b, b) Für (a b, b) ist jedoch h(a-b,b)= a k => h(a-b,b) k=>ggT(a-b,b) ≠ dann mit Definition der FKT ggT(a,b) terminiert auch ggT(a,b) Fall II (a<b) analog Prinzipielle Vorgehensweise Sei f(x) = E die zu untersuchende Fkt. 1.Schritt: 2.Schritt: 2.Beispiel: Zeige, dass unter der Voraussetzung h(x)=0 der Ausdruck E zu einem Ergebnis ≠ führt. (Ind.Schluss) Unter der Voraussetzung h( x) k 1 forme den Ausdruck E so um, dass nur noch rekursive Aufrufe vorhanden sind, für die h(x) k gilt. Schnelle Berechnung von y = mn, m,n IN Naive Implementierung: y m m ... m n 1 Multiplikation Geschicktes Vorgehen: Rückgriff auf bereits berechneten Teilprodukte, Zwei Fälle: I.) y m m ... m m m ... m = A A für geradzahlige n II.) y m m ... m m m ... m m = A A m für geradzahlige n. A A also: n 2 m 2 y 2 n21 m A A für gerades n für ungerades n Damit fkt.: 1. Variante: let rec expo (m,n) = match n with | 0 -> 1 | n when (n mod 2 = 0) -> expo(m,n/2)*expo(m,n/2) | n -> m * expo (m,(n-1)/2)*expo(m,(n-1)/2);; 2. Variante: let expo2 m = let rec aux(n)=match n with | 0 ->1 | n -> let y = aux(n/2) in if (n mod 2 = 0) then y*y else m * y * y in aux ;; Für Terminierung : Mögliche Abstiegsfunktion h(n) = n Bei jedem rek. Aufruf Integer- Division Terminierung bei 0 gewährleistet. Bemerkung: Häufig lässt sich die Terminierung durch strukturelle Ind. schnell zeigen. Vorgehen: - sei f(x) die Fkt. und x M-; - wähle für die Argumente der Fkt eine ’geeignete fundierte Ordnung’ - IA: zeige f(x min) ≠ wo x min die minimalen Elemente der Ordnung sind. - Induktionsschluss: Wenn aus der Gültigkeit von f(z) ≠ für alle z = y die Gültigkeit von f(y) ≠ folgt, dann ist f(x) ≠ für alle Elemente der Ordnung Fkt. terminiert Beispiel 3: Ackermann: n 1 für m 0 A(m, n) A(m 1,1) für m 0, n 0 A(m 1, A(m, n 1)) für m, n 0 - fundierte Ordnung: lexikographische Ordnung über IN0xIN0 also: (0,0)<(0,1)<(0,2)< … <(1,0)<(1,1)<… Damit Beweisführung über geschachtelte Ind. # über m (äußere Ind) # über n bei festem m (innere Ind)) a) b) Ind. anfang: m= 0 A(0,n)=n+1 ≠ Induktionsschritt: Voraussetzung äußere Ind. A(k,n) ≠ für 0 k < m und bel. n - Innere Ind. über n zum Schluss auf m o Anfang n = 0: A(m,0) = A(m-1,1) ≠ nach Voraussetzung der äußeren Induktion o Voraussetzung: A(m,l) ≠ für bestes m und alle l = n o Schluss auf n: A(m,n)= A(m-1,A(m,n-1)) A(m,n-1) terminiert nach Voraussetzung und erzeugt k. also: A(m -1,k) und dies terminiert nach äußerer Voraussetzung Also terminiert A(m,n) für alle drei Rekursionsfälle. Bsp 4: (Klaus Funktion): Rechenvorschrifft, bei der bis heute nicht bekannt ist, ob sie für alle Eingaben terminiert: Ausgehend von a0 IN und a0 > 0 erzeuge Zahlenfolge a0,a1…,an,… in der folgenden Weise: Brich ab, falls an=1 Bsp. Aufruf: für a0 = 3 3,10,5,16,8,4,2,1 Es ist offen, ob bei beliebiger Eingabe a0 die Länge der Liste endlich ist Aufwand von Algorithmen Speicher – bzw. Berechnungsaufwand eines Algorithmus hängen typischer Weise empfindlich von den Parametern ab, die den Algorithmus übergeben werden. Beispielsweise benötigen wir zur Berechnung von fak(n) n rekursive Aufrufe, zur Berechnung der Permutation einer Sequenz der Länge n ca. n! rekursive Aufrufe. Um den Aufwand abschätzen Komplexitätsklassen ein. Definition: zu können g (n) | c 0, n0 IN O( f (n)) mit 0 g (n) c f (n) für n n 0 führt man sogenannte O(f(n)) definiert eine Klasse von Funktionen. Eine Funktion g(n) ist element der Klasse O(f(n)), wenn ab einem bestimmten n0 die Funktion c*f(n) eine andere Schranke für g(n) bildet. Also: g(n) wächst höchstens so schnell wie f(n). Wichtig ist das asymptotische Verhalten für n -> unendlich Beispiel: n2 2 O(n 2 ) n O(n 2 ) n log n O (n 2 ) n3 O(n 2 ) Bei der Fakultätsfunktion ist der Berechnungsaufwand proportional zu n => gehört der Komplexitätsklasse O(n) an.