15.09.2014 Aufrufe

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

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!