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.