Effizienz von Algorithmen - Technische Fakultät - Universität Bielefeld
Effizienz von Algorithmen - Technische Fakultät - Universität Bielefeld
Effizienz von Algorithmen - Technische Fakultät - Universität Bielefeld
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Algorithmen</strong> und Datenstrukturen I<br />
- <strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> -<br />
Alexander Sczyrba<br />
<strong>Technische</strong> <strong>Fakultät</strong><br />
asczyrba@TechFak.Uni-<strong>Bielefeld</strong>.DE<br />
Vorlesung, <strong>Universität</strong> <strong>Bielefeld</strong>, Winter 2013/2014<br />
1 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Kapitel 5 - <strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong><br />
1 <strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong><br />
2 Asymptotische <strong>Effizienz</strong>-Analyse<br />
3 Exkurs<br />
4 Beispiel zur Effizienanalyse<br />
5 Ausnutzen <strong>von</strong> erwarteten Daten-Eigenschaften<br />
2 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Kapitel 5: <strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong><br />
Ziele des Kapitels<br />
Einführung in asymptotische Notationen<br />
asymptotische <strong>Effizienz</strong>analyse (worst/average case)<br />
Wie erfasst man die Problemkomplexität?<br />
Was sind sinnvolle Programmoptimierungen?<br />
asympt. Klasse versus<br />
konstante Faktoren<br />
Analyse diverser Sortierverfahren (insertion-sort,<br />
tree-sort, merge-sort, smooth-sort,<br />
counting-sort))<br />
Einfluss der Laziness auf die <strong>Effizienz</strong><br />
3 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Asymptotische Notationen<br />
Wir betrachten das Polynom<br />
p(x) = 2x 3 + 1000x 2 − 5x + 12<br />
Für große x gilt:<br />
der Beitrag 2x 3 dominiert die anderen Beiträge,<br />
irgendwann auch 1000x 2 .<br />
Für das weitere Wachstum gilt:<br />
p(2 · x) ≈ 8 · p(x)<br />
dafür spielt der Faktor 2 bei x 3 keine Rolle.<br />
4 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Oft interessiert in der Mathematik nur das grobe Wachstumsverhalten<br />
einer Funktion<br />
z.B. bei der Abschätzung <strong>von</strong> Fehlern<br />
(Approximation, fehlerbehaftete Eingaben)<br />
z.B. bei der <strong>Effizienz</strong>analyse <strong>von</strong> <strong>Algorithmen</strong> – siehe Folien zu<br />
Strategien des Problemlösens.<br />
Das “grobe” Verhalten muss natürlich exakt definiert sein . . .<br />
5 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Dazu werden asymptotische Notationen O(n), Ω(n) (Omega), Θ(n)<br />
(Theta) eingeführt.<br />
Sie beschreiben asymptotische <strong>Effizienz</strong>klassen.<br />
(Sie haben aber nicht per se etwas mit <strong>Algorithmen</strong> zu tun, sondern<br />
sind mathematisch definierte Klassen <strong>von</strong> Funktionen mit ähnlichem<br />
Wachstum für n → ∞.)<br />
Die O-Notation wurde <strong>von</strong> Paul Bachmann 1892 eingeführt und<br />
später <strong>von</strong> Edmund Landau weiter bekannt gemacht. Daher wird sie<br />
auch manchmal als Landau-Notation bezeichnet.<br />
Seien f , g : N −→ R + .<br />
6 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
O(g) = {f | ∃C > 0, ∃n 0 ≥ 0, ∀n ≥ n 0 : 0 ≤ f (n) ≤ Cg(n)}<br />
d.h. f gehört zur Klasse O(g), wenn es ab n 0 durch C · g(n) nach<br />
oben beschränkt ist. g(n) soll dabei möglichst einfach beschrieben<br />
sein.<br />
Beispiel:<br />
f (n) = 3n 4 + 5n 3 + 7 log 2 n<br />
f (n) ∈ O(n 4 ), denn<br />
3n 4 + 5n 3 + 7 log 2 n ≤ 3n 4 + 5n 4 + 7n 4 = 15n 4<br />
für n ≥ 1<br />
Wir können C = 15 und n 0 = 1 wählen.<br />
7 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Ebenso gilt:<br />
f (n) ∈ O(n 5 ), schon weil 15n 4 ≤ 15n 5 gilt.<br />
Dagegen gilt:<br />
f (n) /∈ O(n 3 ), denn es gibt keine Konstante C, so dass<br />
3n 4 ≤ C · n 3 für alle n ab irgendeinem n 0 .<br />
Es müsste ja C ≥ 3n gelten.<br />
8 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Aus dem Obigen folgt:<br />
O(1) O(n) O(n 2 ) O(n 3 ) . . .<br />
Die Aussage f ∈ O(n 3 ) ist möglicherweise ausreichend für den<br />
gegebenen Anlass, aber nicht unbedingt genau.<br />
Es könnte ja sein, dass auch f ∈ O(n 2 ) gilt, und das wäre dann<br />
genauer.<br />
9 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Die Theta-Klassen<br />
Die Θ-Klassen begrenzen das Wachstumsverhalten nach oben und<br />
nach unten.<br />
Θ(g) = { f | (∃n 0 , c, C)(∀n ≥ n 0 ) cg(n) ≤ f (n) ≤ Cg(n) }<br />
Ist f ∈ Θ(g), so heißt g asymptotische Schranke <strong>von</strong> f .<br />
Wir finden für f (n) = 3n 4 + 5n 3 + 7 log 2 n<br />
f (n) ∈ Θ(n 4 ) mit den Konstanten C = 15, c = 3 und n 0 = 1<br />
10 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Im Unterschied zu den O-Klassen gilt bei den Θ-Klassen keine<br />
Inklusion.<br />
f (n) /∈ Θ(n 5 ), denn es gibt kein c, n 0 mit c · n 5 ≤ 3n 4 + 5n 3 + 7 log 2 n<br />
für alle n ≥ n 0<br />
Aufgepasst:<br />
Zu gegebenem n 0 lässt sich immer ein c finden mit c · n 5 0 ≤ n4 0 (etwa<br />
c = 1 n 0<br />
), aber dies gilt eben nie für alle n ≥ n 0 .<br />
11 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Asymptotisch gesehen, betrachten wir<br />
f (n) = 3n 4 + 5n 3 + 7 log 2 n als gleichwertig mit n 4 .<br />
Die asymtotischen Effizienklassen haben ungeheuer praktische<br />
Eigenschaften.<br />
12 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Eigenschaften der asymptotischen Klassen<br />
f ∈ Θ(f ) (Reflexivität) (1)<br />
f ∈ Θ(g) ∧ g ∈ Θ(h) ⇒ f ∈ Θ(h) (Transitivität) (2)<br />
f ∈ Θ(g) ⇒ g ∈ Θ(f ) (Symmetrie) (3)<br />
cf ∈ Θ(f ) (4)<br />
n a + n b ∈ Θ(n a ) für a > b (5)<br />
log a n ∈ Θ(log b n) (6)<br />
13 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Zur Übung sollte man einiger dieser Eigenschaften beweisen.<br />
Später geht man ganz routinemäßig damit um – man nutzt<br />
“Rechenregeln” der Form<br />
anstelle der Aussage<br />
O(f ) · O(g) = O(f · g)<br />
h 1 ∈ O(f ) ∧ h 2 ∈ O(g) ⇒ h 1 · h 2 ∈ O(f · g)<br />
(Natürlich meint (f · g)(n) = f (n) · g(n))<br />
14 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
In vielen Büchern wird kaum Θ, sondern hauptsächlich O-Notation<br />
verwendet.<br />
Man sucht die kleinste mögliche O-Klasse anzugeben, das heißt<br />
f ∈ O(g) meint f ∈ Θ(g),<br />
verzichtet aber auf den expliziten Nachweis der unteren Schranke.<br />
15 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische Notationen<br />
Untere und obere asymptotische Schranken<br />
Manchmal gibt man untere und obere Schranken getrennt an, d.h. die<br />
O-Notation wird ergänzt durch Ω:<br />
Ω(g) = { f | (∃n 0 , c)(∀n ≥ n 0 ) cg(n) ≤ f (n) } (7)<br />
In f ∈ O(g) nennt man g obere asymptotische Schranke <strong>von</strong> f .<br />
Bei f ∈ Ω(g) heißt g untere asymptotische Schranke <strong>von</strong> f .<br />
Klar: Θ(g) = Ω(g) ∩ O(g).<br />
16 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Effizienz</strong>analyse<br />
Vorüberlegungen zur <strong>Effizienz</strong>analyse<br />
Nicht für jedes mathematische Problem gibt es einen<br />
Algorithmus.<br />
Wenn es einen gibt, gibt es in der Regel beliebig viele.<br />
Alle (korrekten) <strong>Algorithmen</strong> für das Problem sind mathematisch<br />
äquivalent.<br />
Aus der Sicht der Informatik unterscheiden sie sich erheblich.<br />
17 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Effizienz</strong>analyse<br />
<strong>Algorithmen</strong> sind immaterielle Produkte.<br />
Wenn sie arbeiten, brauchen sie Rechenzeit und belegen<br />
Speicherplatz im Rechner.<br />
<strong>Effizienz</strong>analyse<br />
Die <strong>Effizienz</strong>analyse betrachtet den Zeit- und Platzbedarf <strong>von</strong><br />
<strong>Algorithmen</strong> und liefert analytische Qualitätsurteile (im Unterschied<br />
zu Messungen).<br />
18 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Effizienz</strong>analyse<br />
Was sind geeignete Maßeinheiten?<br />
für Speicherplatz:<br />
Bits, Bytes, MB, Datenblöcke fester<br />
Größe<br />
für Rechenzeit: Befehlszyklen, Sekunden, Tage, . . . ,<br />
Rechenschritte fester Dauer<br />
Das Maß sollte auf den Algorithmus bezogen sein, nicht einen<br />
bestimmten Rechner voraussetzen.<br />
Daher:<br />
Platz in Datenblöcken<br />
Zeit in (abstrakten) Rechenschritten messen<br />
19 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Effizienz</strong>analyse<br />
Datenblöcke und Rechenschritte sind jeweils vernünftig zu wählen:<br />
Länge einer Liste (z.B. beim Sortieren)<br />
Größe einer Zahl (z.B. beim Wurzel ziehen oder einer<br />
Primzahlzerlegung)<br />
Abhängigkeit <strong>von</strong> Größe der Eingabe:<br />
Es ist klar, dass der gleiche Algorithmus “kleinere” Eingaben<br />
schneller verarbeitet als große.<br />
Wir setzen n als Größe der Eingabe gemessen in Datenblöcken und<br />
berechnen Platz und Zeit in Abhängigkeit <strong>von</strong> n.<br />
20 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Average Case und Worst Case<br />
Average Case und Worst Case<br />
Die tatsächliche Laufzeit ist auch manchmal auch abhängig <strong>von</strong> den<br />
genauen Daten. Das haben wir z.B. bei manchen Sortierverfahren<br />
festgestellt.<br />
Auch bei gegebener Eingabe-Größe kann also ein Algorithmus<br />
unterschiedlich lange rechnen oder Platz brauchen.<br />
Worst case Analyse:<br />
längstmögliche Rechenzeit für<br />
Eingabe der Größe n<br />
Average case Analyse: durchschnittliche Rechenzeit<br />
über alle Eingaben der Größe n<br />
21 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Average Case und Worst Case<br />
Worst case<br />
garantiert, dass der Bedarf nie höher ist als angegeben.<br />
Average case nimmt an, dass alle Eingaben gegebener Größe gleich<br />
wahrscheinlich sind, und kann daher täuschen.<br />
22 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Formalisierung<br />
Zusammenfassung und Formalisierung<br />
Gegeben Problem P, Algorithmus A, der P löst.<br />
Sei x die Eingabe für A, und |x| die Größe der Eingabe.<br />
Sei time A (x) Anzahl der Rechenschritte,<br />
space A (x) Anzahl der Datenblöcke<br />
die A zur Berechnung x benötigt.<br />
<strong>Effizienz</strong>aussagen hängen statt <strong>von</strong> x <strong>von</strong> n = |x| ab.<br />
23 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Formalisierung<br />
Worst-case Zeitkomplexität <strong>von</strong> A<br />
wc-time A (n) =<br />
max<br />
{x| n=|x|}<br />
time A (x)<br />
Average-case Zeitkomplexität <strong>von</strong> A<br />
av-time A (n) =<br />
1<br />
|{x| n = |x|}| ·<br />
{x|<br />
∑<br />
n=|x|}<br />
time A (x)<br />
Expected-time Zeitkomplexität <strong>von</strong> A<br />
∑<br />
exp-time A (n) =<br />
{x| n=|x|}<br />
time A (x) · Prob(x)<br />
Dabei ist Prob(x) die Wahrscheinlichkeit der Eingabe x.<br />
24 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Formalisierung<br />
Analog dazu die Platz- oder Speicherkomplexität:<br />
Worst-case Platzkomplexität <strong>von</strong> A<br />
wc-space A (n) =<br />
max<br />
{x| n=|x|}<br />
space A (x)<br />
Average-case Platzkomplexität <strong>von</strong> A<br />
av-space A (n) =<br />
1<br />
|{x| n = |x|}| ·<br />
{x|<br />
∑<br />
n=|x|}<br />
space A (x)<br />
25 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Beispiel: Funktion insert<br />
insert a [ ] = [a]<br />
insert a (x : xs) = if a ≤ x then a : x : xs<br />
else x : insert a xs<br />
Wir bestimmen Zeitbedarf für<br />
in Abhängigkeit <strong>von</strong> n = length x.<br />
insert a x<br />
26 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Nur der Aufruf <strong>von</strong> insert hängt <strong>von</strong> n ab, die Anzahl der sonstigen<br />
Rechenschritte ist konstant. Wir annotieren das Programm<br />
insert a [ ] = [a] {c 1 }<br />
insert a (x : xs) = if a ≤ x then a : x : xs {c 2 }<br />
else x : insert a xs {c 3 }<br />
27 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
wc-time insert (n) = c 1 für n = 0<br />
wc-time insert (n) = c 3 + wc-time insert (n − 1)<br />
da der else-Fall hier der worst case ist.<br />
Daraus erhalten wir<br />
wc-time insert (n) = c 1 + nc 3<br />
28 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Für den average case kommt es darauf an, an welcher Position<br />
eingefügt wird.<br />
Wir nehmen an, alle Positionen sind gleich wahrscheinlich.<br />
Für gegebene Position ist Aufwand exakt berechenbar.<br />
29 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Wir bestimmen<br />
time(n|i) = a wird in Liste der Länge n<br />
nach Position i eingefügt,<br />
i = 0, . . . , n<br />
i = 0 time(n, 0) = c 1<br />
1 ≤ i < n time(n, i) = i · c 3 + c 2<br />
time(n, n) = n · c 3 + c 1<br />
30 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Für den Durchschnitt betrachten wir die n + 1 gleich wahrscheinlichen<br />
Fälle<br />
av-time insert (n) =<br />
=<br />
=<br />
=<br />
1 ( ∑n−1<br />
time(n, 0) + time(n, i) + time(n, n) )<br />
n + 1<br />
i=1<br />
1 ( ∑n−1<br />
)<br />
c1 + (i · c 3 + c 2 ) + n · c 3 + c 1<br />
n + 1<br />
i=1<br />
1 (<br />
n∑ )<br />
2c1 + (n − 1) · c 2 + i · c 3<br />
n + 1<br />
i=1<br />
2<br />
n + 1 c 1 + n − 1<br />
n + 1 c 2 + n 2 c 3<br />
31 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Betrachtung für große n:<br />
lim<br />
n→∞ wc-time insert(n) ≈ n · c 3<br />
lim<br />
n→∞ av-time insert(n) ≈ n 2 · c 3<br />
Hieran sieht man (besser als an der genauen Laufzeitformel):<br />
Aufwand wächst linear mit Größe der Eingabe.<br />
c 3 ist der wesentliche konstante Faktor, der die Laufzeit<br />
bestimmt.<br />
32 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Worst-case für insertion-sort:<br />
isort [ ] = [ ] {k 1 }<br />
isort (a : x) = insert a (isort x) {k 2 }<br />
wc-time isort (0) = k 1<br />
wc-time isort (n + 1) = k 2 + wc-time insert (n) + wc-time isort (n)<br />
= n · k 2 + k 1 +<br />
= n · k 2 + k 1 +<br />
n∑<br />
i=1<br />
wc-time insert (i)<br />
n∑<br />
(c 1 + i · c 3 )<br />
i=1<br />
= n · k 2 + k 1 + n · c 1 +<br />
n(n + 1)<br />
c 3<br />
2<br />
33 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
wc-time isort (n) = n2<br />
2 c 3 + n(c 1 + 1 2 c 3 + k 2 ) + k 1<br />
Also gilt hier<br />
wc-time isort (n) ≈ n2<br />
2 · c 3 für n → ∞<br />
Beachte: c 3 aus insert ist die einzige zeitkritische Konstante.<br />
34 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Die genauen Konstanten c 1 , c 2 , . . . hängen ab <strong>von</strong><br />
der konkreten Rechenmaschine,<br />
der Programmiersprache,<br />
dem Übersetzer.<br />
Sie sind aber jeweils konstant und daher durch<br />
Proportionalitätsfaktoren verknüpft.<br />
Z.B. 1 Funktionsaufruf in Haskell −→ 30 Maschinenbefehle im 1 GHz<br />
Takt<br />
c Aufruf ˆ= 30 · c Maschine ˆ= 30 · 10 −9 sec<br />
35 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Platzbedarf für insert<br />
Hier gibt es keinen Unterschied im worst/average case. Wir setzen<br />
den Platzbedarf für ein Listenelement gleich c.<br />
space insert (0) = c<br />
space insert (n + 1) = (n + 2) · c<br />
also<br />
space insert (n) = (n + 1) · c für n ≥ 0<br />
36 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiele<br />
Platzbedarf für isort<br />
space isort (0) = 0<br />
space isort (n + 1) = space insert (n) + space isort (n)<br />
wäre falsch, denn die Speicherblöcke für insert und isort werden nicht<br />
gleichzeitig benötigt.<br />
Daher:<br />
space isort (n + 1) = max{space insert (n), space isort (n)}<br />
Also:<br />
space isort (n) = space insert (n − 1) = n · c<br />
37 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Asymptotische <strong>Effizienz</strong>-Analyse<br />
Die Komplexität eines Algorithmus A wird häufig nur bis auf konstante<br />
Faktoren bestimmt.<br />
Es interessiert (nur), ob<br />
wc-time A (n) ≈ n<br />
≈ n · log n<br />
≈ n 2<br />
≈ 2 n<br />
und so weiter<br />
Ebenso bei av-time A (n), wc-space A (n), av-space A (n).<br />
38 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Rückblick<br />
Betrachten wir die bisherigen Ergebnisse asymptotisch, so erhalten<br />
wir:<br />
wc-time insert<br />
av-time insert<br />
space insert<br />
∈ Θ(n)<br />
∈ Θ(n)<br />
∈ Θ(n)<br />
wc-time isort ∈ Θ(n 2 )<br />
space isort<br />
∈ Θ(n)<br />
39 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Exakte vs. asymptotische Analyse<br />
Exakte versus asymptotische Analyse<br />
Die bisherige <strong>Effizienz</strong>analyse war exakt – wir konnten genau die<br />
Rolle der Komponenten c 1 , c 2 , c 3 , k 1 , k 2 bestimmen.<br />
Erst am Ende haben wir das asymptotische Verhalten <strong>von</strong><br />
wc-time insert etc. betrachtet.<br />
Die Analyse läßt sich aber auch <strong>von</strong> vornherein asymptotisch<br />
durchführen.<br />
40 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Exakte vs. asymptotische Analyse<br />
wc-time insert ∈ Θ(n) – also setzen wir<br />
wc-time insert (n) = n<br />
Wir setzen k 1 = 0 durch Wahl <strong>von</strong> n.<br />
Wir setzen k 2 = 1 durch Wahl <strong>von</strong> c und C.<br />
Damit vereinfacht sich die Gleichung<br />
zu<br />
wc-time isort (n + 1) = n · k 2 + k 1 +<br />
n∑<br />
i=1<br />
wc-time isort (n + 1) = n +<br />
wc-time isort (n + 1) = n +<br />
wc-time insert (i)<br />
n∑<br />
i<br />
i=1<br />
n(n + 1)<br />
2<br />
∈ Θ(n 2 )<br />
41 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Exakte vs. asymptotische Analyse<br />
Überlegung<br />
Zu den gleichen asymptotischen Ergebnissen kommen wir, wenn wir<br />
in der Analyse nur die Vergleichsoperationen auf Listenelementen<br />
zählen.<br />
→ Warum?<br />
42 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Charakteristische Operation<br />
Definition. Eine Operation p eines Algorithmus A ist<br />
charakteristisch für die asymptotische Laufzeit, wenn<br />
wc-time p_in_A (n) ∈ Θ(wc-time A (n)).<br />
(Beachte: dann gilt auch die Umkehrung.)<br />
Lemma. Eine Operation p in A ist charakteristisch, genau dann,<br />
wenn für alle anderen Operationen q gilt<br />
wc-time q_in_A (n) ∈ O(wc-time p_in_A (n)).<br />
43 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Charakteristische Operation<br />
Beispiel:<br />
In einem Sortierprogramm kommen (:)- und ( n 0 gilt:<br />
Anzahl(:) ≤ 10 · Anzahl(
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Charakteristische Operation<br />
Zwischenfazit<br />
Die <strong>Effizienz</strong>analyse wird durch die asymptotische Betrachtungsweise<br />
stark vereinfacht. Das wesentliche Problem ist das folgende:<br />
Wir müssen die Definition <strong>von</strong> f (x) in Rekurrenzgleichungen für<br />
wc-time(|x|) übersetzen können:<br />
wc-time A (n) = Ausdruck, der wc-time A enthält.<br />
Diese müssen nach wc-time A aufgelöst werden, was nicht immer<br />
einfach ist.<br />
45 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Aufgabe<br />
Zahlenspiel<br />
Das Spiel wird paarweise gespielt. Suche Dir bitte einen<br />
Sitznachbarn, mit dem Du das Spiel spielen kannst.<br />
Einer <strong>von</strong> euch denkt sich jetzt eine Zahl zwischen 0 und 255.<br />
Der andere muss versuchen, die Zahl zu erraten. Er darf aber nur<br />
Ja/Nein-Fragen stellen. Versucht, so wenig Fragen wie möglich zu<br />
stellen.<br />
Wie viele Fragen musstet ihr stellen?<br />
46 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Binärer Baum<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Eine Datenstruktur für binäre Bäume<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Fragen<br />
Wie viele Blätter n gibt es maximal bis Tiefe t?<br />
Wenn ein Baum n Blätter hat, wie tief kann er dann maximal<br />
sein?<br />
Wie ist das Verhältnis <strong>von</strong> Br zu Leaf Knoten?<br />
51 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Überlegungen<br />
Listen sind sehr kompakte Repräsentationen im Speicher und<br />
wir haben interessante Funktionen auf Listen bereits definiert.<br />
Für bestimmte Fragestellungen scheinen sich Bäume besonders<br />
gut zu eignen.<br />
Kann man die beiden Datentypen ineinander überführen?<br />
52 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Tree and back again. . .<br />
Für die Überführung <strong>von</strong> Listen in Bäume brauchen wir eine Funktion<br />
build :: [a] -> Tree a<br />
Und für die Überführung <strong>von</strong> Bäumen in Listen etwa<br />
leaves :: Tree a -> [a]<br />
53 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Naiver Ansatz für leaves, ganz entlang einer Strukturellen Rekursion<br />
auf Bäumen:<br />
leaves :: Tree a -> [a]<br />
leaves Nil = []<br />
leaves (Leaf a) = [a]<br />
leaves (Br l r) = leaves l ++ leaves r<br />
Betrachten wir einmal die Zahl der Rechenschritte unserer<br />
naiven Implementierung.<br />
wc-time leaves (n) = n 2<br />
Diese Funktion leaves ist also sehr teuer bezüglich der<br />
Rechenzeit.<br />
54 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Ansatz unter Vermeidung der Listenkonkatenation:<br />
leaves’ :: Tree a -> [a]<br />
leaves’ Nil = []<br />
leaves’ (Leaf a) = [a]<br />
leaves’ (Br Nil r) = leaves’ r<br />
leaves’ (Br (Leaf a) r) = a:leaves’ r<br />
Was machen wir, wenn links ebenfalls ein Branch hängt?<br />
Idee: wir wandeln den Branch um in ein einfacheres Problem!<br />
leaves’ (Br (Br l’ r’) r) = leaves’ (Br l’ (Br r’ r))<br />
55 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Exkurs Bäume Ende<br />
. . . und nun zurück zum Thema<br />
56 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Als nächstes (interessanteres) Beispiel betrachten wir das<br />
Programm sortTree. Es berechnet die sortierte Liste der Blätter eines<br />
Baumes:<br />
sortTree :: Tree Integer -> [Integer]<br />
sortTree Nil = []<br />
sortTree (Leaf a) = [a]<br />
sortTree (Br l r) = merge (sortTree l) (sortTree r)<br />
merge :: (Ord a) => OrdList a -> OrdList a -> OrdList a<br />
merge [] bs = bs<br />
merge as [] = as<br />
merge (a:as) (b:bs)<br />
| a
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Wie beschreibt man den worst-case genau?<br />
merge (as, bs) [· · · , a m , b n ]<br />
oder [· · · , b n , a m ]<br />
Alle Elemente (bis auf das letzte) werden vor dem Einbau verglichen.<br />
59 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Nun folgt die Analyse <strong>von</strong> sortTree. Wir betrachten nun Bäume mit n<br />
Blättern:<br />
60 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Die Aufrufe <strong>von</strong> merge ergeben sich aus der Baumstruktur<br />
61 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zwischenbeobachtung:<br />
In der Tat kann man so rechnen, dass die “merge-Bäume” als<br />
Zwischenergebnisse auftreten. Man wendet überall die Gleichungen<br />
<strong>von</strong> sortTree an – und keine anderen.<br />
Damit ist auch der Aufwand bis zu diesem Punkt bekannt – n − 1<br />
innere Knoten (Br) erfordern genau n − 1 Gleichungsanwendungen,<br />
unabhängig <strong>von</strong> der Form des Baums.<br />
62 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Die gleiche Betrachtung an isort[4, 2, 3, 1] ergibt das<br />
Zwischenergebnis:<br />
insert<br />
4<br />
insert<br />
2<br />
insert<br />
3<br />
insert<br />
1<br />
[ ]<br />
Worin liegt noch der Unterschied zwischen isort und sortTree bei<br />
Listen-ähnlichem Baum?<br />
63 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Betrachtet man den Rechenaufwand für die merge-Phase ergibt sich:<br />
bzw.<br />
Resultat: 3 + 2 + 1 = 6 1 + 1 + 3 = 5<br />
Wir müssen also da<strong>von</strong> ausgehen, dass wc-time sortTree <strong>von</strong> der Form<br />
des Baumes abhängt.<br />
64 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Die Größe der Daten n für sortTree(t) können wir beschreiben durch<br />
die Anzahl size(t) der Blätter <strong>von</strong> t (Form unbeschränkt, aber die<br />
Tiefe muss dann zwischen log 2 n und size(t) liegen)<br />
die Tiefe depth(t) des Baumes t (die Anzahl der Blätter ist damit<br />
beschränkt durch 2 depth(t) )<br />
Wir untersuchen beide Varianten.<br />
65 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung basierend auf size<br />
Untersuchung ausgehend <strong>von</strong> size(t)<br />
Sei n = size (t)<br />
wc-time sortTree (1) = 0<br />
wc-time sortTree (n) = n − 1 + max { wc-time sortTree (i)+<br />
wc-time sortTree (n − i)|<br />
0 < i < n }<br />
Wie kommen wir weiter?<br />
66 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung basierend auf size<br />
Wir programmieren wc-time sortTree in Haskell und tabellieren:<br />
n 1 2 3 4 5 6 7 8 9<br />
wc-time sortTree (n) 0 1 3 6 10 15 21 28 36<br />
Das sieht aus wie 1 2 n(n − 1). 67 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung basierend auf size<br />
In der Tat – der schlechteste Fall tritt auf für i = 1 und i = n − 1 – das<br />
sind “entartete” Baum, etwa<br />
wc-time sortTree (n) = 1 2 n(n − 1) ∈ Θ(n2 )<br />
68 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung ausgehend <strong>von</strong> depth<br />
Untersuchung ausgehend <strong>von</strong> depth(t)<br />
Sei d = depth(t)<br />
wc-time sortTree (0) = 0<br />
wc-time sortTree (d + 1) = 2 d+1 − 1 + 2wc-time sortTree (d)<br />
wc-time sortTree (d) = ∑ d<br />
i=1 2d−i (2 i − 1)<br />
= ∑ d<br />
i=1 (2d − 2 d−i ) = d2 d − 2 d + 1 ∈ Θ(d2 d )<br />
Schlechtester Fall: ausgeglichener Baum. Dieser hat n = 2 d Blätter,<br />
wir haben also wc-time sortTree (n) ∈ Θ(n · log 2 n)<br />
– das ist besser als Θ(n 2 ) wie zuvor ermittelt.<br />
69 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung ausgehend <strong>von</strong> depth<br />
Sei n Anzahl der Blätter, d die Tiefe <strong>von</strong> t.<br />
Wir haben erhalten:<br />
wc-time sortTree (n) ∈ Θ(n 2 )<br />
wc-time sortTree (d) ∈ Θ(2 d · d)<br />
Im ersten Fall ist im worst-case d = n.<br />
Im zweiten Fall ist im worst-case n = 2 d .<br />
Beide Analysen zusammengenommen:<br />
wc-time sortTree (n, d) ∈ Θ(n · d)<br />
70 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Untersuchung ausgehend <strong>von</strong> depth<br />
Konsequenz:<br />
sortTree ist asymptotisch schneller als insertion sort, sofern<br />
der Baum ausgeglichen ist: Dann ist d = ⌈log 2 n⌉.<br />
Können wir aus einer (unsortierten) Liste l in Θ(n · log n) Zeit oder<br />
schneller einen ausgeglichenen Baum aufbauen (⇒ Funktion<br />
build)? Dann erhalten wir durch<br />
sortTree(build(l))<br />
ein schnelleres Sortierverfahren in Θ(n · log n).<br />
71 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Aufbau eines ausgeglichenen Baums<br />
build :: [a] -> Tree a<br />
build [] = Nil<br />
build [a] = Leaf a<br />
build as = Br (build (take k as))(build (drop k as))<br />
where k = length as ‘div‘ 2<br />
Auch dieser naive Ansatz (wie bei leaves) ist recht<br />
teuer. . . warum?<br />
In der letzten Definition werden die as mehrfach durchlaufen<br />
(length, take, drop).<br />
72 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Asymptotische <strong>Effizienz</strong> <strong>von</strong> build<br />
Zählen wir (wie bisher) nur die Vergleichsoperationen, so gilt:<br />
Was stimmt hier nicht?<br />
wc-time build (m) = 0<br />
Der Vergleich ist nicht charakteristische Operation für build.<br />
Also zählen wir die Schritte für take, drop, length, build.<br />
73 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
wc-time take (k, n) = k ∈ Θ(n)<br />
wc-time drop (k, n) = k ∈ Θ(n)<br />
wc-time length (n) = n + 1 ∈ Θ(n)<br />
wc-time build (0) = 1<br />
wc-time build (n) = n + 1 + 2n + 2 · wc-time build (n/2)<br />
= 3n + 1 + 2 · (n/2 + 1 + 2n/2<br />
+2 · wc-time build (n/4))<br />
74 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Insgesamt kann (log 2 n)-mal halbiert werden, jedesmal kommt ein<br />
Beitrag in Θ(n) hinzu, also<br />
wc-time build (n) ∈ Θ(n · log n)<br />
75 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Ergebnis: Ein schnelles Sortierverfahren<br />
mergeSort :: [a] → OrdList a<br />
mergeSort = sortTree.build<br />
wc-time mergeSort (n) ∈ Θ(n · log n)<br />
76 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Vergleich des asymptotischen Wachstums:<br />
n n log 2 n n 2<br />
10 3 ≈ 10, 0 · 10 3 10 6<br />
10 4 ≈ 13, 3 · 10 4 10 8<br />
10 5 ≈ 16, 6 · 10 5 10 10<br />
10 6 ≈ 19, 9 · 10 6 10 12<br />
mergeSort ist also wesentlich effizienter als insertionSort.<br />
77 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Lässt sich mergeSort weiter verbessern?<br />
Aber worauf sollen wir abzielen –<br />
bessere konstante Faktoren bei Laufzeit Θ(n · log n)?<br />
bessere asymptotische Laufzeit, etwa Θ(n) oder<br />
Θ(n · log(log n))?<br />
78 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Problemkomplexität<br />
Definition<br />
Die algorithmische Komplexität eines Problems P ist die<br />
<strong>Effizienz</strong>klasse des besten Algorithmus, der P löst.<br />
“Beste” meint hier: niedrigste asymptotische worst-case-Laufzeit. Das<br />
ist in der Regel eine Klasse <strong>von</strong> <strong>Algorithmen</strong> mit unterschiedlichen<br />
konstanten Faktoren.<br />
79 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
build<br />
Kochrezept zum Nachweis <strong>von</strong>:<br />
P hat Komplexität Θ(f )<br />
1 Angabe eines Algorithmus A für P mit wc-time A ∈ Θ(f )<br />
2 Zeigen, dass jeder Algorithmus für P mindestens Θ(f ) Schritte<br />
rechnen muss.<br />
80 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel<br />
Satz:<br />
Sortieren durch Vergleichen (wir hatten (
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel<br />
Eine Liste mit n Elementen hat n! Permutationen. Wir kennen daher<br />
die Mindest-Tiefe des Entscheidungsbaums. Also gilt:<br />
Time sort (n) ≥ log 2 (n!)<br />
Die <strong>Fakultät</strong>sfunktion lässt sich mit der Stirlingschen Formel<br />
abschätzen:<br />
n! ≥ √ ( n<br />
2πn<br />
e<br />
) n<br />
.<br />
83 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel<br />
Insgesamt erhalten wir:<br />
(√ (<br />
Time sort (n) ≥ log 2 2πn n<br />
) ) n<br />
e<br />
= log 2<br />
√<br />
2πn + log2<br />
( n<br />
e<br />
) n<br />
= log 2 (2πn) 1/2 + log 2<br />
( n<br />
e<br />
) n<br />
= 1 2 log 2 (2πn) + n log 2 n e<br />
= 1 2 log 2 (2π) + 1 2 log 2 n + n log 2 n − n log 2 e<br />
∈ Θ(n log n)<br />
84 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Der Weg zu einer guten Problemlösung<br />
1 Man verschafft sich Klarheit über die Komplexität des zu<br />
lösenden Problems.<br />
2 Man entwickelt einen Algorithmus, dessen <strong>Effizienz</strong> in der Klasse<br />
der Problemkomplexität liegt. Asymptotisch gesehen, ist dieser<br />
bereits “optimal”.<br />
3 Man analysiert die konstanten Faktoren des Algorithmus und<br />
sucht diese zu verbessern.<br />
85 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Programmoptimierung am Beispiel mergeSort<br />
Unter Optimierung <strong>von</strong> Programmen versteht man im Allgemeinen<br />
die Verbesserung der konstanten Faktoren. (Verbessert sich die<br />
asymptotische <strong>Effizienz</strong>klasse, spricht man eher <strong>von</strong> einem Redesign<br />
des Algorithmus.)<br />
Wir bilden Varianten <strong>von</strong> build und vergleichen die konstanten<br />
Faktoren.<br />
86 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
build :: [a] -> Tree a<br />
build [] = Nil<br />
build [a] = Leaf a<br />
build (as) = Br (build (take k as)) (build (drop k as))<br />
where k = length as ’div’ 2<br />
Die Neuberechnung der Listenlänge (length) kostet Θ(n log n)<br />
Schritte!<br />
87 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Eine Funktion mit zusätzlichem Parameter (⇒ Einbettung) hilft, die<br />
Neuberechnung der Länge vermeiden:<br />
build’’ :: [a] -> Tree a<br />
build’’ as = buildn (length as) as<br />
where buildn :: Int -> [a] -> Tree a<br />
buildn 1 (a:as) = Leaf a<br />
buildn n as = Br (buildn k (take k as))<br />
(buildn (n-k) (drop k as))<br />
where k = n ‘div‘ 2<br />
Jedoch: auch take und drop erzeugen Aufwand der Ordnung<br />
Θ(n · log n).<br />
88 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Überlegung: Die n Elemente der Eingabe werden dauernd in immer<br />
kleinere Listen gepackt, um am Ende in den Baum gehängt zu<br />
werden.<br />
Können wir diese Listen vermeiden, und jedes Element der Eingabe<br />
nur einmal “anfassen”, wenn es in den Baum gehängt wird?<br />
89 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Idee: Jeder Aufruf <strong>von</strong> build nimmt <strong>von</strong> der Liste was er braucht,<br />
und reicht den Rest unbesehen weiter:<br />
build’ :: [a] -> Tree a<br />
build’ as = fst (buildSplit (length as) as)<br />
buildSplit 1 (a:as) = (Leaf a, as)<br />
buildSplit n as = (Br l r, as’’)<br />
where k = n ‘div‘ 2<br />
(l,as’) = buildSplit k as<br />
(r,as’’) = buildSplit (n-k) as’<br />
90 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Analyse:<br />
wc-time buildSplit (n) = 6 + wc-time buildSplit (⌊n/2⌋) + wc-time buildSplit (⌈n/2⌉)<br />
wc-time buildSplit (1) = 6<br />
(Die zweite 6 ist etwas zu hoch, vereinfacht aber die Rechnung.)<br />
Für n = 2 k : wc-time buildSplit (2 k ) = 6(2 k+1 − 1) = 12n − 6 ∈ Θ(n)<br />
91 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Ergebnis für mergeSort:<br />
Wir haben die <strong>Effizienz</strong> der build-Phase <strong>von</strong> Θ(n log n) nach Θ(n)<br />
verbessert.<br />
An der asymptotischen <strong>Effizienz</strong> <strong>von</strong> mergeSort ändert sich dadurch<br />
nichts, da die sortTree-Phase in Θ(n · log n) bleibt.<br />
Aber besser als Θ(n log n) geht ja auch nicht . . .<br />
92 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Weitere Vereinfachung:<br />
Wir brauchen build, um einen ausgeglichenen Baum aus der<br />
Eingabeliste zu machen.<br />
Der Baumstruktur entspricht genau die Aufrufstruktur <strong>von</strong> merge.<br />
Wir können den Baum ganz eliminieren (Stichwort “Deforestation”).<br />
Wir nehmen die Definition <strong>von</strong> mergeSort und rechnen den Baum<br />
weg:<br />
93 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
mergeSort [] = sortTree (build []) = sortTree Nil = []<br />
mergeSort [a] = sortTree (build [a]) = sortTree (Leaf a) = [a]<br />
mergeSort (as) = sortTree (build as)<br />
= sortTree Br (build (take k as)) (build (drop k as)))<br />
where k = length as ‘div‘ 2<br />
= merge (sortTree (build (take k as))<br />
sortTree (build (drop k as)))<br />
where k = length as ‘div‘ 2<br />
= merge (mergeSort (take k as)<br />
mergeSort (drop k as))<br />
where k = length as ‘div‘ 2<br />
94 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Übrig bleibt also<br />
mergeSort’ [] = []<br />
mergeSort’ [a] = [a]<br />
mergeSort’ (as) = merge (mergeSort (take k as))<br />
(mergeSort (drop k as))<br />
where k = length as ‘div‘ 2<br />
Dieser mergeSort baut keinen Baum auf. Die zuvor untersuchten<br />
Verbesserungen <strong>von</strong> build sollte man auch hier wieder<br />
untersuchen.<br />
95 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Zusammenfassung<br />
Vergleicht einmal den Effekt:<br />
mtest = mergeSort [1..10000]<br />
für die alte und neue Version <strong>von</strong> mergeSort.<br />
96 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Ausnutzen <strong>von</strong> erwarteten Daten-Eigenschaften<br />
In den obigen Testbeispielen ist die Eingabe-Liste bereits sortiert –<br />
wo<strong>von</strong> unser Programm aber wenig mitkriegt.<br />
Wir suchen eine Variante <strong>von</strong> mergeSort, die umso schneller wird, je<br />
mehr die Eingabe vorsortiert ist.<br />
97 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Idee: Ausnutzung <strong>von</strong> “Läufen” (Runs) <strong>von</strong> vorsortierten<br />
Elementen (aufsteigend oder absteigend).<br />
Problem: Top-Down Baumkonstruktion zerstört die Läufe.<br />
Lösung: Wir bestimmen erst die Läufe und bauen aus ihnen den<br />
Baum.<br />
98 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Läufe sind auf- oder absteigende Folgen. Über die Richtung<br />
entscheidet das zweite Element.<br />
16 14 13 4 9 10 11 5 1 15 6 2 3 7 8 12<br />
16 14 13 4 | 9 10 11 | 5 1 | 15 6 2 | 3 7 8 12.<br />
99 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
runs :: [a] -> [[a]]<br />
runs [] = [[]]<br />
runs [a] = [[a]]<br />
runs (a:b:x) = if a [a] -> [a] -> [[a]]<br />
ascRun a as [] = [reverse (a:as)]<br />
ascRun a as (b:y) = if a
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Nun wird ein ausgeglichener Baum aufgebaut, an dessen Blättern die<br />
Läufe stehen. Er hat also den Typ Tree[a] statt Tree a.<br />
Trotzdem können wir die schon bekannte Θ(n) build-Funktion<br />
einsetzen – für diese ist n nun die Anzahl der Läufe!<br />
101 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Das Programm smoothSort:<br />
smsort :: Ord a => [a] -> [a]<br />
smsort = mergeRuns . build’ . runs<br />
mergeRuns :: Ord a => Tree [a] -> [a]<br />
mergeRuns (Leaf x) = x<br />
mergeRuns (Br l r) = merge (mergeRuns l) (mergeRuns r)<br />
102 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Sortieren in Θ(n)<br />
Eine Funktion sort :: Ord(a) ⇒ [a] → OrdList a kann es nicht<br />
geben mit <strong>Effizienz</strong> Θ(n).<br />
Einzige vorausgesetzte Operationen auf dem Datentyp a sind ja die<br />
Vergleichsoperationen der Typklasse Ord.<br />
Wissen wir mehr über den Typ a, lässt sich dies vielleicht ausnutzen.<br />
103 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Dazu brauchen wir aus der Haskell-Vorlesung<br />
die Typklasse (Ix a) (Index-Typen)<br />
den Datentyp Array a b (mit Index-Typ a und Elementtyp b<br />
Das Besondere an Arrays: Elementzugriff<br />
Array t : t!i ∈ Θ(1) im Unterschied zu<br />
Liste l : l!!i ∈ Θ(i)<br />
104 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Idee des Counting-Sort:<br />
Alle Elemente liegen in einem begrenzten Intervall.<br />
Wir zählen, wie oft jeder Wert vorkommt.<br />
Wir reproduzieren alle Werte in aufsteigender Reihenfolge und<br />
korrekter Anzahl.<br />
105 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel: Eingabe “bacaabfdfabaf”<br />
Intervall: [a..f]<br />
Tabelle<br />
Ausgabe: “aaaaabbbcdfff”<br />
Anzahl<br />
a |||||<br />
b |||<br />
c |<br />
d |<br />
e<br />
f |||<br />
106 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
<strong>Effizienz</strong><br />
Der Aufbau der Tabelle sowie die Erzeugung der sortierten Ausgabe<br />
lassen sich in Θ(n) bewerkstelligen.<br />
CountingSort ist ein lineares Sortierverfahren<br />
Haskell-Code:<br />
import Array<br />
countingSort :: Ix a => (a,a) -> [a] -> [a]<br />
countingSort bs x = [ a | (a,n)
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Effekte der Laziness<br />
Was wir bisher über <strong>Effizienz</strong>analyse und Komplexität gelernt haben,<br />
gilt für alle Programmiersprachen. Schließlich gibt es<br />
immer eine geeignete abstrakte Einheit “Rechenschritt”, auf<br />
der die Analyse aufbaut.<br />
109 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Eine Besonderheit gilt für Haskell als eine Programmiersprache, in<br />
der das Prinzip der “Lazy Evaluation” (verzögerte Auswertung)<br />
realisiert ist.<br />
Die bisherige Analyse ging da<strong>von</strong> aus, dass es bei jedem Aufruf, z.B.<br />
isort x, stets das ganze Ergebnis berechnet wird. Laziness kann<br />
dazu führen, dass auch weniger gerechnet wird.<br />
110 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Grundbegriffe zu Auswertungs-Strategien:<br />
Rechnen = Anwenden <strong>von</strong> Gleichungen in Formeln<br />
Redex = reducible expression: Stelle in einer Formel, an<br />
der die linke Seite einer Gleichung “passt”<br />
“passt” = die auf der linken Seite verlangten Konstruktoren<br />
der Argumente liegen vor<br />
111 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel:<br />
head (x:xs) = x<br />
ist anwendbar auf die Formel und ergibt<br />
head (1:2:2:[]) => 1<br />
head (1:([3,4]++[5,6])) => 1<br />
head ((1+4):[5]) => (1+4)<br />
head (1:ones) where<br />
ones = 1:ones => 1<br />
ist (noch) nicht anwendbar auf<br />
head ([1,3,4] ++ [5,6])<br />
head (map (1+) [1,2,3])<br />
head ones where<br />
ones = 1:ones<br />
Es muss erst eine Gleichung für (++), map oder ones angewandt<br />
werden.<br />
112 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Lage <strong>von</strong> Redexen in einer Formel<br />
Ein Redex ist<br />
innermost, wenn er keinen weiteren Redex enthält,<br />
outermost, wenn er in keinem weiteren Redex enthalten ist,<br />
“zwischendrin”, sonst.<br />
113 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel<br />
114 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Auswertungs-Strategien<br />
leftmost innermost: immer den Redex wählen, der am weitesten<br />
links steht und innermost ist.<br />
leftmost outermost: immer den Redex wählen, der am weitesten<br />
links steht und outermost ist.<br />
gemischt: man kann sich viele weitere Strategien vorstellen . . .<br />
115 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Besonderheit <strong>von</strong> if_then_else:<br />
Seine definierenden Gleichungen sind<br />
Ein Redex<br />
if True then x else y = x<br />
if False then x else y = y<br />
wird in jedem Fall leftmost outermost berechnet.<br />
Je nach Ergebnis für C wird A oder B nicht berechnet.<br />
116 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Diese Regel für if_then_else gilt in allen Programmiersprachen.<br />
Ohne sie könnte man keine terminierende Rekursion programmieren:<br />
f (x) = if_then_else(x ≤ 0, 42, 2 + f (x − 1))<br />
Innermost-Strategie führt zu endloser Berechnung des else-Falles.<br />
Auch wenn die Bedingung gerne in “mix-fix” Schreibweise<br />
daherkommt:<br />
f (x) = if x ≤ 0 then 42 else 2 + f (x − 1)<br />
sollte man nicht verkennen, dass es sich dabei um eine dreistellige<br />
Funktion handelt, die NICHT innermost berechnent wird.<br />
117 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Eigenschaften der Strategien<br />
1 Ob eine Rechnung terminiert, hängt i.A. <strong>von</strong> der Strategie ab.<br />
2 Wenn zwei verschiedene Strategien, angewandt auf die gleiche<br />
Formel, terminieren, liefern sie das gleiche Ergebnis.<br />
3 Im Fall (2) kann sich der Rechenaufwand stark unterscheiden.<br />
4 Wenn für eine Formel F irgendeine Strategie terminiert, dann<br />
terminiert für F auch “leftmost outermost”.<br />
5 “leftmost innermost” terminiert seltener als “leftmost outermost”.<br />
118 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Anschauliche Bedeutung<br />
innermost: Alle Argumente einer Funktion werden ganz<br />
ausgerechnet, bevor die Funktion “aufgerufen” wird.<br />
outermost: Die Argumente einer Funktion werden immer nur so<br />
weit ausgerechnet, wie es die Funktion für ihren<br />
nächsten Schritt braucht.<br />
119 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Lazy Evaluation<br />
Haskell verwendet lazy evaluation (verzögerte Auswertung), das ist<br />
leftmost outermost + graph reduction<br />
graph reduction ist eine Zusatzregel, die die Duplikation<br />
unausgerechneter Formeln vermeidet.<br />
120 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Beispiel zur graph reduction<br />
twice x = x ∗ x<br />
twice (3 + 4)<br />
↑<br />
↑<br />
outermost innermost<br />
würde blindlings outermost reduziert zu<br />
(3 + 4) ∗ (3 + 4)<br />
wo nun der Ausdruck (3 + 4) zweimal berechnet werden müsste.<br />
121 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Stattdessen wird reduziert<br />
twice (3 + 4) ⇒ x ∗ x<br />
where x = 3 + 4<br />
worin durch die “Nebenrechnung” 3 + 4 nur einmal berechnet wird.<br />
122 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Einfluss <strong>von</strong> lazy evaluation auf <strong>Effizienz</strong>-Analyse<br />
Es wurde bisher angenommen, dass alle Funktionen ihre Ergebnisse<br />
ganz berechnen.<br />
Das muss nicht stimmen. Unter lazy evaluation kann es sein, dass<br />
eine Funktion in einem bestimmten Kontext nur einen Teil ihres<br />
Ergebnisses berechnet. Die Laufzeit kann dann besser sein als nach<br />
der Analyse ohne Berücksichtigung der Laziness.<br />
123 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Hier die naheliegende Implementierung <strong>von</strong> minimum:<br />
minimum (a:as) = min a as<br />
min a [] = a<br />
where<br />
min a (b:x) = if a
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Hier eine alternative Implementierung, die bequemerweise den<br />
Insertion-Sort benutzt<br />
minimum (a:as) = head (isort (a:as))<br />
Wegen wc-time isort (n) ∈ Θ(n 2 ) könnte man vermuten, dass<br />
wc-time minimum (n) ∈ Θ(n 2 ).<br />
Sehen wir genauer hin!<br />
125 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Die Definition <strong>von</strong> isort war:<br />
isort [] = []<br />
isort (a:as) = insert a (isort as)<br />
insert a [] = [a]<br />
insert a (b:as) = if a
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Lazy Evaluation berechnet in einer Formel immer an der “äußersten”<br />
Stelle, an der eine Gleichung anwendbar ist:<br />
Zunächst kann immer nur die Gleichung isort.2 angewandt werden,<br />
am Ende einmal isort.1<br />
head<br />
head<br />
isort<br />
insert<br />
a1<br />
:<br />
a 2<br />
:<br />
:<br />
=><br />
a 1<br />
insert<br />
a2<br />
insert<br />
a n<br />
[ ]<br />
a n<br />
[ ]<br />
127 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Danach wird n-mal insert angewandt, beginnend bei a n . Nehmen<br />
wir an, a 5 ist das kleinste Element. Streng “outermost” entsteht nun<br />
head<br />
:<br />
=><br />
a 5<br />
a 1<br />
insert<br />
insert<br />
a2<br />
insert<br />
innerhalb des Kastens<br />
könnte man weiterrechnen,<br />
aber das wäre nicht<br />
“outermost”.<br />
a n−1<br />
[a n]<br />
128 / 130
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
head braucht nicht mehr als den obersten Konstruktur (:), um sein<br />
Ergebnis zu liefern<br />
head<br />
=> a 5<br />
:<br />
a 5<br />
Es werden ≈ 2 · n Gleichungen angewandt. Der Rest der Θ(n 2 )<br />
Operationen steckt im Kasten und wird nicht benötigt!<br />
129 / 130<br />
<strong>Effizienz</strong> <strong>von</strong> <strong>Algorithmen</strong> Asymptotische <strong>Effizienz</strong>-Analyse Exkurs Beispiel zur Effizienanalyse Ausn<br />
Fazit: Durch Lazy Evaluation gilt auch bei dieser Definition<br />
wc-time minimum (n) ∈ Θ(n).<br />
130 / 130