Prozeduren und von ihnen erzeugte Prozesse Gültigkeitsbereiche

Werbung
Prozeduren und von ihnen erzeugte Prozesse
Gültigkeitsbereiche und Rekursion
. . . direkte Umsetzung in Scheme
(define (fact n)
(if (= n 1)
; 1! = 1
1
(* n (fact (- n 1)))))
• Stand: Wir kennen
– Elementare (“primitive”) Operationen
– Kombination dieser Operationen
– Abstraktion zusammengesetzter Operationen
Hinweis: Ausführliche Diskussion in Algorithmik 1, Kap. 8
• Analogie zum Schachspiel: Wir kennen die Regeln, aber nicht
• (Partielle) Korrektheit gemäss Spezifikation
– auf welche Züge es ankommt
(welche Prozeduren wir wie kombinieren sollen)
– was die Konsequenzen eines Zuges sind
(was die Ausführung einer Prozedur bewirkt)
• Zusammenhang zwischen Rekursionsschema und vollständiger Induktion
• Termination
Keine zirkuläre Definition: fact wird nicht durch “sich selbst” definiert
⇒ “vermindertes” Teilproblem
• Beachte: Lokales vs. globales Verhalten
G. Görz, FAU, Inf.8
3–1
Lineare Rekursion und Iteration
3–3
Anwendung des Substitutionsmodells auf die Berechnung
von (fact 6)
Prozedur: Schema für die lokale Entwicklung eines Berechnungsprozesses
Beispiel: Fakultätsfunktion, n ∈ N
n! = n · (n − 1) · (n − 2) · . . . · 3 · 2 · 1
Also:
1! = 1 und n! ist n · (n − 1)! für alle n > 1
n! = n · ((n − 1) · (n − 2) · . . . · 3 · 2 · 1)
= n · (n − 1)!
G. Görz, FAU, Inf.8
G. Görz, FAU, Inf.8
3–2
(fact 6)
(* 6 (fact 5))
(* 6 (* 5 (fact 4)))
(* 6 (* 5 (* 4 (fact 3))))
(* 6 (* 5 (* 4 (* 3 (fact 2)))))
(* 6 (* 5 (* 4 (* 3 (* 2 (fact 1))))))
(* 6 (* 5 (* 4 (* 3 (* 2 1)))))
(* 6 (* 5 (* 4 (* 3 2))))
(* 6 (* 5 (* 4 6)))
(* 6 (* 5 24))
(* 6 120)
720
⇒ Linear rekursiver Prozeß zur Berechnung von 6!
G. Görz, FAU, Inf.8
3–4
Lineare Rekursion: Anmerkungen
Linear iterativer Prozeß
Motivation: Wir nehmen — im Beispiel — eine andere Perspektive ein:
Anmerkungen zum Berechnungsprozeß:
• Zwei Phasen: Expansion und Kontraktion
Beschreibung einer Regel zur Berechnung von n!, indem wir zuerst 1 mit 2,
dann das Ergebnis mit 3, dann mit 4, etc.
multiplizieren, bis wir n erreicht haben.
• Während der Expansion werden Berechnungsschritte “aufgeschoben”:
(n − 1) Multiplikationen — Buchhaltung über “Zwischeninformation”!
D.h.: Wir halten in jedem Schritt ein Produkt fest zusammen mit einem
Zähler, der von 1 bis n läuft.
Aufwand der Buchhaltung hier linear proportional zu n
• In der Kontraktion werden die Multiplikationen ausgeführt
• Es gibt keine “Zwischenergebnisse” — Zwischenzustände ergeben sich
aus der Buchhaltung
G. Görz, FAU, Inf.8
3–5
In jedem Schritt ändern sich der Zähler und das Produkt gleichzeitig nach
der Regel
product ← counter · product
counter ← counter + 1,
wobei n! gleich dem Wert des Produkts ist, wenn der Zähler n
überschreitet.
G. Görz, FAU, Inf.8
Beispiel: Fakultät iterativ
Lineares Rekursionsschema
(define (fact n)
1. Finde heraus, wie ein Rekursions-Schritt durchzuführen ist.
2. Zerlege das Problem in “Arbeitsschritt” und “vermindertes” Teilproblem
3. Lege fest, wann das (schon gelöste) Basisproblem erreicht ist
(Rekursionsende)
Schema für linear rekursive Funktionen:
; Blockstruktur!
(define (fact-iter product counter)
(if (> counter n)
product
(fact-iter (* counter product)
(+ counter 1)) ))
(fact-iter 1 1))
(define (f_linrec ...)
(cond (P <STOP>)
{ (Q (g (f_linrec ...))) }
(else (h (f_linrec ...))) ))
G. Görz, FAU, Inf.8
3–7
3–6
G. Görz, FAU, Inf.8
3–8
Beispiel: Linear iterativer Prozeß
Schema für rest-rekursive Funktionen
Linear iterativer Prozeß zur Berechnung von 6! (erste Version)
auch: end-rekursiv, rumpf-rekursiv (“tail recursive”)
(fact 6)
(fact-iter
1 1)
(fact-iter
1 2)
(fact-iter
2 3)
(fact-iter
6 4)
(fact-iter 24 5)
(fact-iter 120 6)
(fact-iter 720 7)
720
(define (f_tailrec ...)
(cond (P <STOP>)
{ (Q (f_tailrec ...)) }
(else (f_tailrec ...)) ))
Anforderung an jede Scheme-Implementation:
Rest-rekursive Prozeduren müssen intern als iterative Prozesse ausgeführt
werden
⇒ keine Belastung der internen Buchhaltung
Hinweis: Verschränkte Restrekursion (Beispiel: Algorithmik 1)
G. Görz, FAU, Inf.8
3–9
Anmerkungen zum Berechnungsprozeß
3–11
Äquivalenz der linear-rekursiven und der rest-rekursiven
Version
• Keine verschiedenen Phasen
• “Zwischeninformation” in den Variablen product, counter,
max-count (bzw. n)
(define (fact n)
(if (= n 1) 1
(* n (fact (- n 1)))))
• Zwischenergebnisse werden explizit in der Akkumulatorvariablen
product festgehalten (akkumuliert)
• Aufwand ist ebenfalls linear (Sequentielle Aufzählung der Elemente des
Definitionsbereichs: natürliche Zahlen)
• Der durch fact-iter verursachte Berechnungsprozeß ist iterativ;
die Prozedur fact selbst jedoch (rest-) rekursiv
• “Iterativ” vs. “rekursiv” in Bezug auf Prozesse betrifft die Buchhaltung
und die Aufschiebung von Berechnungsschritten
G. Görz, FAU, Inf.8
G. Görz, FAU, Inf.8
3–10
(define (fact-i n product)
(if (= n 1) product
(fact-i (- n 1) (* product n))))
Vereinfachte Variante der rest-rekursiven Fakultätsfunktion: zählt abwärts
Beh.: ∀n ≥ 1. (fact n) = (fact-i n 1)
G. Görz, FAU, Inf.8
3–12
Baumrekursion
n = 1:
(fact 1) = 1 = (fact-i 1 1)
n > 1:
(fact n)
= (* n (fact (- n
= (* n (fact-i (= (fact-i (- n 1)
= (fact-i (- n 1)
= (fact-i n 1)
Beispiel: Fibonacci (Leonardo von Pisa: Kaninchenaufgabe)
1)))
n 1) 1))
(* n 1))
(* 1 n))
Definition
Induktionsannahme
Lemma
Rekursionsgleichungen:
F0 = 0
Definition
F1 = 1
Fn = Fn−1 + Fn−2
Lemma:
(* a (fact-i n z)) = (fact-i n (* a z))
n = 1:
(* a (fact-i 1 z)) = (* a z)
= (fact-i 1 (* a z))
G. Görz, FAU, Inf.8
n > 1:
(* a (fact-i n z))
= (* a (fact-i (- n 1) (* z n)))
= (fact-i (- n 1) (* a (* z n)))
= (fact-i (- n 1) (* (* a z) n))
= (fact-i n (* a z))
G. Görz, FAU, Inf.8
ergibt die Folge: 0 1 1 2 3 5 8 13 21 34 55 89 . . .
3–13
G. Görz, FAU, Inf.8
3–15
Als Scheme-Prozedur:
Definition
Induktionsannahme
Assoziativität
Definition
3–14
(define (fib n)
(cond
((= n 0) 0)
((= n 1) 1)
(else
(+ (fib (- n 1))
(fib (- n 2)))) ))
G. Görz, FAU, Inf.8
3–16
Fibonacci-Baum
Baum-Rekursion
Die Fibonacci-Prozedur ist ein charakteristisches Beispiel für
Baum-Rekursion: (fib 20) = 6765, (fib 30) = 832040
• Folge der Verarbeitungsschritte bildet strukturell den zu verarbeitenden
Baum nach !
⇒ “baum-rekursiv”
• Schema für baum-rekursive Funktionen:
(define (f_treerec ...)
...
(G (f_treerec ...) (f_treerec ...) ...)
...)
G. Görz, FAU, Inf.8
3–17
G. Görz, FAU, Inf.8
3–19
Fibonacci iterativ
Fibonacci-Baum (Forts.)
• Der Fibonacci-Baum enthält fib(n + 1) Blätter: fib(0) bzw. fib(1)
• Wie groß ist fib(n) ungefähr?
Die baum-rekursive Fibonacci-Prozedur ist in höchstem Maß ineffizient,
Sei φ definiert durch:
φ2 = φ + 1
√
also: φ = (1 + 5)/2 ≈ 1.618 (“goldenes Verhältnis”)
√
Dann: fib(n) ≈ φn/ 5
⇒ Umbau in eine iterative Prozedur mit zwei Akkumulatorvariablen a, b:
D.h., der Fibonacci-Baum wächst exponentiell in Bezug auf die
Blätteranzahl: exponentiell abhängig von n
Die meiste Zeit wird zur (erneuten) Berechnung bereits berechneter
Zwischenergebnisse verbraucht; z.B. die Hälfte zur Berechnung von
Fn−2, was bereits bei der Berechnung von Fn−1 verwendet worden war.
• Speicherbedarf = Anzahl der Ebenen im Baum: linear proportional zu n
G. Görz, FAU, Inf.8
3–18
Anfangswerte a = 1, b = 0
Simultane Transformationen
G. Görz, FAU, Inf.8
a
b
←a+b
←a
3–20
Iterationsschritte:
n
n
n
n
n
n
n
i
=
=
=
=
=
=
=
1
2
3
4
5
6
7
a
a
a
a
a
a
a
=
=
=
=
=
=
=
1
1
2
3
5
8
13
fib(i)
Aufwände von Algorithmen und Komplexitätsmaße
b
b
b
b
b
b
b
=
=
=
=
=
=
=
0
1
1
2
3
5
8
Rekapitulation: Algorithmik 1, Kap. 13
Um die Effizienz von Algorithmen vergleichen zu können, braucht man eine
Abschätzung der Zeit oder des Speicherplatzes, die sie abhängig von der
benötigen.
Sei n ein Eingabeparameter (“Größe der Eingabe”); es werden die
Funktionen
T (n) ≈ Zeitbedarf
Ressourcen
P (n) ≈ Platzbedarf
untersucht, wobei die Größenordnung “O” des Ressourcenbedarfs
(Ordnung des Aufwands) interessiert.
fib(i − 1)
G. Görz, FAU, Inf.8
3–21
G. Görz, FAU, Inf.8
Fibonacci: rest-rekursiv
Aufwände der rekursiven Beispiele
(define (fib n)
Rekursive Fakultät:
T (n) = O(n)
P (n) = O(n)
(define (fib-iter a b count)
(if (= count 0)
b
(fib-iter
(+ a b)
a
(- count 1))))
Iterative Fakultät:
T (n) = O(n)
P (n) = O(1)
Rekursive Fibonacci-Prozedur:
T (n) = O(φn)
P (n) = O(n)
(fib-iter 1 0 n))
Iterative Fibonacci-Prozedur:
Rest-rekursiv: konstanter Speicherbedarf, linearer Zeitbedarf
(“Zwischenwerte” werden jeweils nur einmal berechnet!)
G. Görz, FAU, Inf.8
3–23
T (n) = O(n)
P (n) = O(1)
3–22
G. Görz, FAU, Inf.8
3–24
Schnelle Exponentiation
Beispiel: Exponentiation (Potenzbildung)
Verwendung von Quadraten, z.B. b8 = (b4)2 = ((b2)2)2
Basis b, Exponent n (integer ≥ 0)
Umformung allgemein:
0
b =1
bn = b · bn−1
bn =
Rekursive Fassung:
P (n) = O(n)
G. Görz, FAU, Inf.8
3–25
G. Görz, FAU, Inf.8
(define (expt b n)
T (n) = O(log n)
P (n) = O(log n)
(define (expt-iter counter product)
(if (= counter 0) product
(expt-iter (- counter 1)
(* b product))))
(expt-iter n 1))
G. Görz, FAU, Inf.8
3–27
b2n benötigt eine Multiplikation mehr als bn, damit:
Iterative Fassung:
T (n) = O(n)
(bn/2)2 ← n gerade
b · bn−1 ← n ungerade
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n)
(square (fast-expt b (/ n 2))))
(else
(* b (fast-expt b (- n 1))))))
(define (expt b n)
(if (= n 0) 1
(* b (expt b (- n 1)))))
T (n) = O(n)
z.B. n = 1000: 14 Multiplikationen
P (n) = O(1)
3–26
G. Görz, FAU, Inf.8
3–28
Ein weiteres Beispiel: Primzahltest
Primzahltest (3)
(define (prime? n)
(define (smallest-divisor n) ; n eliminierbar ...
(find-divisor n 2))
(define (find-divisor n test-divisor)
(cond ((> (square test-divisor) n) n)
((divides? test-divisor n) test-divisor)
(else
(find-divisor n (+ test-divisor 1)))))
(define (divides? a b)
(= (remainder b a) 0))
(= n (smallest-divisor n)))
• Vorgehen
– Suche die kleinste Zahl ≥ 2, die n teilt.
– n ist Primzahl ⇔ die gefundene Zahl ist n selbst.
• Um eine Rekursion zu ermöglichen, wird die Fragestellung
verallgemeinert: Suche die kleinste Zahl ≥ t, die n teilt.
Ansatz: Ist t2 > n ?
– ja: nur n selbst teilt n
– nein: t teilt n ?
∗ ja: t ist der kleinste Teiler von n
∗ nein: Suche weiter ab t + 1
G. Görz, FAU, Inf.8
Aufwandsordnung
der Schrittzahl, um n als Primzahl zu identifizieren:
√
T (n) = O( n)
3–29
G. Görz, FAU, Inf.8
Schneller Primzahltest: Fermat-Test
Primzahltest (2)
Spezifikationsskizze:
Zwei Zahlen heißen kongruent modulo n, wenn sie beide bei der Division
durch n denselben Rest haben.
1. n ist Primzahl, falls gilt:
t teilt n → t = 1 ∨ t = n, d.h. n hat keine echten Teiler
Der Divisionsrest einer Zahl a bei der Division durch n heißt auch der
Rest von a modulo n oder einfach a modulo n.
2. Jede Zahl hat mindestens zwei Teiler.
Für Primzahlen gilt: zweitkleinster-Teiler(n) = n
Kleiner Fermatscher Satz:
Wenn n Primzahl und a natürliche Zahl mit 2 ≤ a < n, so gilt:
3. zweitkleinster-Teiler(n): kleinster Teiler von n, der größer ist als 1
(smallest-divisor n)
4. Prozedur “finde-größeren-Teiler”: (find-divisor n test-divisor)
test-divisor ist der kleinste Teiler, der versucht wird.
√
Abbruchkriterium: Ist n keine Primzahl, so muß es einen Teiler ≤ n
haben.
√
Denn: Falls d teilt n, so auch n/d teilt n; entweder d oder n/d ≤ n
G. Görz, FAU, Inf.8
3–31
3–30
an ≡ a mod n
Idee des Fermat-Tests:
Wähle ein a < n und berechne Rest von an mod n.
Falls Rest = a, dann ist n eventuell eine Primzahl, andernfalls keine.
Iteriert ⇒ “probabilistischer” Algorithmus
G. Görz, FAU, Inf.8
3–32
Beispiel zum Fermat-Test
Fermat-Test vervollständigt
Beispiel: n = 5
15
25
35
45
55
=
=
=
=
=
(define (fermat-test n)
≡ 1 mod 5
≡ 2 mod 5
≡ 3 mod 5
≡ 4 mod 5
≡ 0 mod 5
1
32
243
1024
3125
(define a (+ 2 (random (- n 2))))
(= (exptmod a n n) a))
Gegenbeispiel: n = 4
4
1 =
24 =
34 =
1
16
81
Idee zum Zeitgewinn: Wähle a (1 < a < n) zufällig aus mittels
Zufallszahlen-Generators (random r).
≡ 1 mod 4
≡ 0 mod 4
≡ 1 mod 4
Test mit mehreren, aber nicht allen a.
Jedes a, das den Test besteht, ist ein Indiz für n prim.
G. Görz, FAU, Inf.8
3–33
exptmod
G. Görz, FAU, Inf.8
3–35
(define (fast-prime? n times)
(cond ((= times 0) #t)
((fermat-test n)
(fast-prime? n (- times 1)))
(else #f)))
Für den Fermat-Test wird der Rest von be mod m gesucht, d.h.
be ≡ x mod m
Erster Ansatz:
√
Aufwand: O(log n) gegenüber O( n) für prime? !
(define (exptmod2 b e m)
(modulo (fast-expt b e) m))
exptmod2 ist ungeeignet für den Fermat-Test, denn z.B. beim Test von
997 wird benötigt:
a997 ≡ a mod 997 ?
Aber: (fast-expt a 997) für a < 997 funktioniert nur für kleine
a (≤ 4) ⇒ Gesucht: ein “kleineres” exptmod! (s. Anhang)
G. Görz, FAU, Inf.8
3–34
G. Görz, FAU, Inf.8
3–36
Anmerkungen zu fast-prime?
Zur Komplexitätsabschätzung
Gut: f (n) ist Polynom (polynomiale Komplexität)
• Verfahren ist probabilistisch: Ergebnis #t gibt Evidenz für Primzahl,
keine Sicherheit
Schlecht: sonst (exponentielle Komplexität)
• Es gibt Zahlen, die das Verfahren irreführen: 561,
1105,. . . (Carmichaelsche Zahlen — 16 Stück < 100.000 bei 8500
Primzahlen)
Beachte: “worst case” Abschätzungen (ungünstigster Fall)
Exponentielle Lösungen können sehr wohl brauchbar sein, wenn:
• Es gibt Varianten des Verfahrens, die nicht in die Irre geführt werden
können
• ein Programm nur wenige Male verwendet wird;
• ein Programm nur mit “kleinen” Eingaben benutzt wird;
• Es gibt Verfahren mit geringerer Fehlerwahrscheinlichkeit als
Wahrscheinlichkeit eines Hardwarefehlers während Laufzeit
• eine effizientere Lösung ist so kompliziert ist, dass sie nicht mehr
modifiziert werden kann;
• Primzahltests haben praktischen Nutzen, z.B. sind sie in der
Kryptographie Basis der meisten modernen “unbreakable Codes”
G. Görz, FAU, Inf.8
• Zeiteffizienz auf Kosten von Platzeffizienz geht.
3–37
G. Görz, FAU, Inf.8
3–39
Abschließende Bemerkungen
• Die meisten der behandelten rekursiven Prozesse (Ausnahme z.B. fib)
haben dieselbe Ordnung im Wachstum von Zeit- und Speicherbedarf
ANHANG
ZUR SELBSTÄNDIGEN NACHARBEIT
• Bezeichnungen für die gängigen Fälle:
Iteration für konstanten Platzbedarf
Rekursion für nicht-konstanten Platzbedarf
Lineare Iteration und lineare Rekursion für Prozesse, bei denen alle
Anforderungen — mit Ausnahme des Platzbedarfs für einen
Iterationsschritt — linear sind
G. Görz, FAU, Inf.8
3–38
G. Görz, FAU, Inf.8
3–40
Ein “kleineres” exptmod
exptmod (Forts.)
Das Problem:
be ≡ x mod m
(define (exptmod b e m)
(cond ((= e 0) 1)
((even? e)
(remainder (square
(exptmod b (/ e 2) m))
m))
(else
(remainder (* b
(exptmod b (- e 1) m))
m))))
Falls e gerade:
be = (be/2)2
dann:
be ≡ (be/2)2 mod m
≡ a2 mod m
mit (be/2)2 ≡ a mod m
G. Görz, FAU, Inf.8
3–41
Entsprechend für e ungerade:
G. Görz, FAU, Inf.8
3–43
Vorgehen analog zu fast-expt — gleiche Rekursionsidee:
be = b · be−1
dann:
G. Görz, FAU, Inf.8
• Fallunterscheidung für gerade und ungerade Exponenten
• sukzessives Quadrieren, sodass die Anzahl der Schritte logarithmisch mit
dem Exponenten wächst
be ≡ a1 · a2 mod m
mit b
≡ a1 mod m
be−1 ≡ a2 mod m
T (n) = O(log n)
3–42
G. Görz, FAU, Inf.8
P (n) = O(log n)
3–44
Herunterladen