Laufzeitanalyse: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung Markierung: Zurückgesetzt |
Keine Bearbeitungszusammenfassung Markierung: Zurückgesetzt |
||
| Zeile 2: | Zeile 2: | ||
== Einführung == | == Einführung == | ||
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]] | [[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]] | ||
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]] | |||
Die '''Laufzeitanalyse''' untersucht, wie viele Rechenschritte ein [[Algorithmus]] benötigt, um ein Problem zu lösen. | Die '''Laufzeitanalyse''' untersucht, wie viele Rechenschritte ein [[Algorithmus]] benötigt, um ein Problem zu lösen. | ||
Version vom 20. April 2026, 09:16 Uhr
Einführung


Die Laufzeitanalyse untersucht, wie viele Rechenschritte ein Algorithmus benötigt, um ein Problem zu lösen.
Da die tatsächliche Laufzeit eines Programms stark vom Computer abhängt (z. B. Prozessor oder Arbeitsspeicher), betrachtet man nicht die Zeit in Sekunden, sondern die Anzahl der benötigten Operationen.
Diese Anzahl hängt von der Größe der Eingabe ab. Die Eingabegröße wird meist mit [math]\displaystyle{ n }[/math] bezeichnet.
Beispiel:
- Beim Sortieren ist [math]\displaystyle{ n }[/math] die Anzahl der zu sortierenden Werte.
- Beim Durchsuchen einer Liste ist [math]\displaystyle{ n }[/math] die Länge der Liste.
Für kleine Eingaben spielt die Laufzeitanalyse oft kaum eine Rolle, weil selbst langsame Verfahren schnell genug sind. Wichtig wird sie bei großen Datenmengen.
Die Frage lautet dann:
Wie wächst der Aufwand, wenn die Eingabe immer größer wird?
Genau das beschreibt die asymptotische Laufzeit.
Wenn sich die Laufzeit eines Algorithmus durch eine Funktion beschreiben lässt, z. B.
[math]\displaystyle{ f(x)=\frac{1}{x}+x }[/math]
dann erkennt man für große [math]\displaystyle{ x }[/math], dass der Anteil [math]\displaystyle{ \frac{1}{x} }[/math] immer kleiner wird. Das Verhalten der Funktion wird dann fast nur noch durch [math]\displaystyle{ x }[/math] bestimmt.
Deshalb betrachtet man bei der Laufzeitanalyse nur das Verhalten für große Werte von [math]\displaystyle{ n }[/math]. Dieses Verhalten beschreibt man mit der Landau-Notation (Groß-O-Notation), z. B.:
[math]\displaystyle{ \mathcal{O}(n) }[/math], [math]\displaystyle{ \mathcal{O}(n^2) }[/math], [math]\displaystyle{ \mathcal{O}(n \log n) }[/math]
Damit kann man Algorithmen vergleichen, ohne von einer bestimmten Hardware abhängig zu sein.
Wichtige Abgrenzung
Die asymptotische Laufzeitanalyse gibt nicht an, wie viele Sekunden ein Programm wirklich benötigt.
Sie beschreibt nur, wie stark der Aufwand wächst, wenn die Eingabemenge größer wird. Das macht verschiedene Algorithmen vergleichbar.
Szenarien der Laufzeitabschätzung
Bei der Laufzeitanalyse betrachtet man meist drei Fälle:
- Best Case (bester Fall):
Der Algorithmus arbeitet unter idealen Bedingungen und benötigt minimalen Aufwand.
- Worst Case (schlechtester Fall):
Der Algorithmus trifft auf die ungünstigste Eingabe und benötigt maximalen Aufwand.
- Average Case (durchschnittlicher Fall):
Man betrachtet den durchschnittlichen Aufwand bei zufälligen Eingaben.
Diese drei Fälle helfen dabei, das Verhalten eines Algorithmus besser einzuschätzen.
Average-Case und Erwartungswert
Der Average-Case beschreibt die durchschnittliche Laufzeit.
Mathematisch wird diese mit dem Erwartungswert berechnet:
[math]\displaystyle{ E(T)=\sum_{i=1}^{|E|} p_i \cdot t_i }[/math]
Dabei gilt:
- [math]\displaystyle{ t_i }[/math]: Laufzeit bei einer bestimmten Eingabe
- [math]\displaystyle{ p_i }[/math]: Wahrscheinlichkeit dieser Eingabe
Der Erwartungswert ist also:
Laufzeit × Wahrscheinlichkeit
für alle möglichen Eingaben zusammengezählt.
Oft nimmt man an, dass alle Eingaben gleich wahrscheinlich sind. Dann spricht man von einer Gleichverteilung.
---
Beispiel: Lineare Suche
Bei der linearen Suche wird eine Liste von vorne nach hinten durchsucht.
Steht das gesuchte Element:
- an Position 1 → 1 Vergleich
- an Position 2 → 2 Vergleiche
- ...
- an Position [math]\displaystyle{ n }[/math] → [math]\displaystyle{ n }[/math] Vergleiche
Wenn jede Position gleich wahrscheinlich ist, ergibt sich:
[math]\displaystyle{ E(T)=\frac{1}{n}\sum_{i=1}^{n} i }[/math]
Mit der Gaußschen Summenformel:
[math]\displaystyle{ \sum_{i=1}^{n} i = \frac{n(n+1)}{2} }[/math]
folgt:
[math]\displaystyle{ E(T)=\frac{1}{n}\cdot\frac{n(n+1)}{2}=\frac{n+1}{2} }[/math]
Im Durchschnitt benötigt die lineare Suche also etwa halb so viele Vergleiche wie im schlechtesten Fall.
Für große [math]\displaystyle{ n }[/math] dominiert der Term [math]\displaystyle{ n }[/math], daher gilt:
[math]\displaystyle{ \mathcal{O}(n) }[/math]
Beispiel: Laufzeitanalyse von Insertion Sort
Beim Insertion Sort wird jedes neue Element an der richtigen Stelle in den bereits sortierten Teil eingefügt.
Best Case
Der beste Fall liegt vor, wenn die Liste bereits sortiert ist:
[1,2,3,4,5]
Dann genügt pro Element ein Vergleich.
Bei [math]\displaystyle{ n }[/math] Elementen:
[math]\displaystyle{ T(n)=n-1 }[/math]
Das Wachstum ist linear:
[math]\displaystyle{ \mathcal{O}(n) }[/math]
Worst Case
Der schlechteste Fall liegt vor, wenn die Liste rückwärts sortiert ist:
[5,4,3,2,1]
Dann muss jedes neue Element an den Anfang verschoben werden.
Die Anzahl der Vergleiche ist:
[math]\displaystyle{ 1+2+3+\dots+(n-1) }[/math]
Mit Gauß:
[math]\displaystyle{ \frac{n(n-1)}{2} }[/math]
Das ergibt quadratisches Wachstum:
[math]\displaystyle{ \mathcal{O}(n^2) }[/math]
Average Case
Bei zufälliger Reihenfolge liegt das neue Element im Durchschnitt ungefähr in der Mitte des bereits sortierten Bereichs.
Daher braucht man im Mittel etwa halb so viele Vergleiche wie im Worst Case:
[math]\displaystyle{ \frac{1}{2}\cdot\frac{n(n-1)}{2} }[/math]
Das ist ebenfalls proportional zu [math]\displaystyle{ n^2 }[/math].
Deshalb gilt auch hier:
[math]\displaystyle{ \mathcal{O}(n^2) }[/math]
Der konstante Faktor ist zwar kleiner als im Worst Case, die Komplexitätsklasse bleibt aber gleich.
Wichtige Erkenntnis
Bei der Groß-O-Notation interessieren nur:
- das grundsätzliche Wachstum,
- die höchste Potenz.
Konstanten werden ignoriert.
Beispiele:
- [math]\displaystyle{ 3n }[/math] → [math]\displaystyle{ \mathcal{O}(n) }[/math]
- [math]\displaystyle{ \frac{1}{2}n^2 }[/math] → [math]\displaystyle{ \mathcal{O}(n^2) }[/math]
Deshalb können zwei Algorithmen unterschiedlich schnell sein, aber trotzdem dieselbe Komplexitätsklasse besitzen.