Informatik · Klasse 11–13 · aktualisiert
Landau-Notation: O, Omega und Theta einfach erklärt
Du lernst O, Ω und Θ als obere, untere und enge Schranke, vereinfachst Kostenfunktionen und vergleichst Algorithmen.
0 von 13 Aufgaben gelöst
Kurz gesagt
Die Landau-Notation beschreibt, wie die Schrittzahl eines Algorithmus bei sehr großen Eingaben wächst, ohne Konstanten. O ist eine obere, Ω eine untere und Θ eine enge Schranke. Bei einer Summe zählt nur der größere Summand: O(n³ + n² + 1) = O(n³).
Zwei Programme lösen dieselbe Aufgabe. Bei zehn Daten merkst du keinen Unterschied, bei sehr vielen Daten aber schon. Die Landau-Notation sagt dir, wie stark der Aufwand mit der Datenmenge wächst, egal auf welchem Computer das Programm läuft.
Deine Lernziele
Hake ab, was du schon kannst. Löst du alle Aufgaben eines Abschnitts, hakt Mela das Ziel für dich ab.
4 Abschnitte
Landau-Symbole beschreiben, wie eine Funktion für sehr große Eingaben wächst. Man nennt das asymptotisches Verhalten. In der Informatik geben sie ein Maß für die Anzahl der Elementarschritte (einfache Rechenschritte) in Abhängigkeit von der Größe n des Problems an. Konstanten spielen dabei keine Rolle.
Es gibt drei Symbole. O ist die obere Schranke: f wächst höchstens genauso schnell wie g. Ω ist in der Komplexitätstheorie die untere Schranke: f wächst mindestens genauso schnell wie g. Θ ist die enge Schranke: f wächst genauso schnell wie g. Θ bedeutet beides zugleich: f liegt in O(g) und in Ω(g).
Genauer: f(n) = O(g(n)) bedeutet f(n) ≤ c·g(n) für alle n ≥ n₀ und ein c > 0. Bei Ω steht ≥ statt ≤. Bei Θ liegt f(n) ab einem n₀ zwischen zwei Vielfachen von g(n), mit zwei positiven Konstanten c₁ und c₂. Außerdem gilt in der Komplexitätstheorie: f ∈ Ω(g) genau dann, wenn g ∈ O(f).
Eine Hilfe ist der Quotient f(n)/g(n) für wachsendes n. Strebt er gegen einen Wert kleiner als unendlich, gilt f = O(g). Strebt er gegen einen Wert größer als 0, gilt f = Ω(g). Strebt er gegen eine Konstante c mit c > 0, wächst f genauso schnell wie g.
Vorsicht bei der Schreibweise: f = O(g) ist keine Gleichung, sondern nur symbolisch gemeint. O(g) ist eine Menge von Funktionen, deshalb ist f(n) ∈ O(g(n)) formal korrekt. Aus f₁ = O(g) und f₂ = O(g) folgt nicht, dass f₁ und f₂ gleich sind. Das Symbol Ω hat in der Zahlentheorie eine andere Definition; hier gilt die aus der Komplexitätstheorie.
Definition
Θ (Theta)
f ∈ Θ(g) heißt: f wächst genauso schnell wie g. Es gilt sowohl f ∈ O(g) als auch f ∈ Ω(g).
| Symbol | Schranke | f wächst … wie g |
|---|---|---|
| O | obere Schranke | höchstens genauso schnell |
| Ω | untere Schranke | mindestens genauso schnell |
| Θ | enge Schranke | genauso schnell |
Beispiel: Gilt 2ⁿ⁺¹ = O(2ⁿ)?
- 1Gesucht ist ein c mit 2ⁿ⁺¹ ≤ c·2ⁿ.
- 2Es ist 2ⁿ⁺¹ = 2·2ⁿ.
- 3Mit c = 2 gilt 2·2ⁿ ≤ 2·2ⁿ für alle n ≥ 0.
- 4Also gilt 2ⁿ⁺¹ = O(2ⁿ).
Teste dich
Eine Kostenfunktion T(n) gibt hier die Anzahl der Datensatzvergleiche bei Problemgröße n an. Dabei interessiert nicht die Zeit eines Programms auf einem bestimmten Computer, sondern wie der Bedarf bei mehr Daten wächst. Meist sind große n wichtig, weil sich kleine Problemgrößen oft nur im Millisekundenbereich unterscheiden.
Zwei Regeln helfen. Additionsregel: Bei einer Summe zählt nur der größere Summand, O(f(n)+g(n)) wird zu O(max(f(n), g(n))). Konstantenregel: Ein konstanter Faktor c > 0 fällt weg, O(c·f(n)) wird zu O(f(n)). Beide Regeln gelten entsprechend für Θ. Der Summand, der übrig bleibt, ist der dominierende Term.
Welcher Summand am schnellsten wächst, siehst du an der Reihenfolge der Wachstumsprototypen: log(n) wächst langsamer als n, dieses langsamer als n·log(n), dann folgen n², n³ und so weiter. Die Basis des Logarithmus ist dabei egal.
| Wachstum | Prototyp f(n) | Verhalten |
|---|---|---|
| logarithmisch | log(n) | Verdoppelt sich n, wächst f(n) um einen konstanten Betrag |
| linear | n | Verdoppelt sich n, verdoppelt sich f(n) |
| logarithmisch-linear | n·log(n) | Wächst schneller als n, aber langsamer als n² |
| quadratisch | n² | Verdoppelt sich n, vervierfacht sich f(n) |
| kubisch | n³ | Verdoppelt sich n, verachtfacht sich f(n) |
| exponentiell | bⁿ | Steigt n um 1, vervielfacht sich f(n) mit b |
Beispiel: Selectionsort: T₁(n) = n²/2 − n/2
- 1T₁(n) = n·(n−1)/2 = n²/2 − n/2
- 2Summanden: n²/2 und −n/2. Der Term n²/2 wächst am schnellsten, er dominiert.
- 3Der Faktor 1/2 ist eine Konstante und fällt weg: übrig bleibt n².
- 4Kontrolle: Der Quotient (n²/2 − n/2) : n² strebt gegen die Konstante 1/2, also gegen einen Wert größer als 0.
- 5Ergebnis: T₁ wächst genauso schnell wie n², also T₁ ∈ Θ(n²).
Merke: Streiche konstante Faktoren und alle langsamer wachsenden Summanden. Der Rest ist die Größenordnung, zum Beispiel O(500·n³) = O(n³).
Teste dich
Die Laufzeit hängt manchmal nicht nur von n ab, sondern auch davon, wie stark eine Liste bereits vorsortiert ist. Darum unterscheidet man drei Fälle. Best Case ist der Fall mit den wenigsten Kosten. Worst Case ist der Fall mit den meisten Kosten. Average Case ist eine Mittelung der Kosten über alle Fälle. Der Average Case ist für die Praxis meist am wichtigsten, aber schwer zu ermitteln. Der Worst Case ist meist der relevantere der beiden Extremfälle. Spricht man einfach von Zeitkomplexität, ist meistens der Worst Case gemeint.
Beispiel Suche: In einer Liste mit zwanzig Namen wird von vorn gesucht, bis der Name gefunden ist. Best Case: Die Suchzeit ist 1, wenn der Name der erste ist. Worst Case: Die Suchzeit ist 20, wenn der Name der letzte ist. Sie stellt sich auch ein, wenn der Name nicht in der Liste ist. Average Case: 10,5, wenn der Name definitiv in der Liste ist. Beim Quicksort sind es angenähert Tbest(n) = n·log₂(n) im günstigsten und Tworst(n) = n·(n−1)/2 im ungünstigsten Fall.
So analysierst du eine Schleife: Jede einfache Anweisung kostet O(1). Dass ein print mehr Ressourcen braucht als eine Zuweisung, wird in der Landau-Notation vernachlässigt. Läuft eine Schleife n-mal und steht im Rumpf eine feste Anzahl einfacher Anweisungen, multiplizierst du: O(f(n))·O(g(n)) wird zu O(f(n)·g(n)). Danach addierst du alle Teile und behältst nur den größten. Wichtig: n muss für die Größe der Eingabe stehen, zum Beispiel als Parameter. Ist n im Code fest auf 5 gesetzt, läuft die Schleife immer 5-mal, und der Aufwand ist konstant.
Fall und Schranke sind zwei verschiedene Fragen. Der Fall sagt, welche Eingabe du betrachtest. Das Symbol O, Ω oder Θ sagt, welche Art von Schranke du für die Kosten dieses Falls angibst. Das folgt aus den Definitionen oben. Weil man meist am Worst Case interessiert ist, gibt man dafür meist eine obere Schranke an. Das ist aber nur eine Gewohnheit: Auch für den Best Case des Insertionsort gibt man eine obere Schranke an, nämlich O(n).
Definition
Worst Case
Der Worst Case ist der Fall, in dem bei der Ausführung die meisten Kosten anfallen. Der Best Case hat die wenigsten Kosten, der Average Case mittelt die Kosten über alle Fälle.
Beispiel: Schleife mit Parameter n (ausgedachte Variante)
- 1def ausgabe(n): # n steht für die Eingabegröße
- 2 m = 5 # O(1)
- 3 for i in range(0, n, 1): # n Durchläufe
- 4 print('Hallo Welt') # O(1)
- 5 print(m) # O(1)
- 6 m += 1 # O(1)
- 7 print('Ende') # O(1)
- 8Rechnung: O(1) + O(n)·(O(1)+O(1)+O(1)) + O(1) = O(1) + O(3n) + O(1) = O(n)
Merke: Der Fall legt fest, welche Eingabe gemeint ist. O, Ω und Θ legen fest, welche Art von Schranke du für die Kosten dieses Falls angibst.
Teste dich
Wachstum allein reicht für einen Vergleich nicht. Du musst auch fragen, welche Vorbedingungen ein Algorithmus braucht.
Die lineare Suche geht die Liste Element für Element durch, bis sie das Element gefunden hat. Sie liegt in O(n), weil sie im schlechtesten Fall (gesuchter Wert nicht gefunden) n Vergleiche benötigt. Sie funktioniert auch in unsortierten Arrays. Wenn die Liste klein ist, ist sie oft auch das effizienteste Verfahren.
Die binäre Suche halbiert die Länge des Suchbereichs in jedem Schritt. Ihre Zeitkomplexität ist O(log n). In einem Array mit n Einträgen sind maximal ⌈log₂(n+1)⌉ Vergleichsschritte nötig (aufgerundet). Sie ist deutlich schneller als die lineare Suche, aber sie hat Vorbedingungen. Die Elemente müssen nach einer totalen Ordnungsrelation angeordnet (sortiert) sein. Außerdem muss die Datenstruktur direkten Zugriff auf das n-te Element erlauben. Auf einer einfachen verketteten Liste würde die Effizienz verloren gehen. Und die Ordnung muss zur Suche passen: Gesucht wird nach dem Schlüssel, nach dem sortiert ist.
Beispiel mit 13 sortierten Buchstaben: Bis der Buchstabe G gefunden war, mussten nur 3 Buchstaben verglichen werden. Auch im schlechtesten Fall hätten nur 4 verglichen werden müssen. Ein naiver Algorithmus, der die Liste von vorne nach hinten durchgeht, müsste im ungünstigsten Fall bis zu 13 Elemente untersuchen. Der Vorteil der binären Suche fällt umso stärker ins Gewicht, je größer die Liste ist.
Globale Aussagen über Kostenfunktionen sind oft nicht möglich: In einem Bereich kann die eine günstiger sein, in einem anderen die andere. Darum gehören zu einem Vergleich immer beide Fragen: Wie wächst der Aufwand, und was muss vorher erfüllt sein?
| Kriterium | Lineare Suche | Binäre Suche |
|---|---|---|
| Zeitkomplexität | O(n) | O(log n) |
| Vorbedingung | keine Sortierung nötig | sortiert, direkter Zugriff auf das n-te Element |
| Schlechtester Fall bei 13 Elementen | bis zu 13 Elemente | 4 Buchstaben vergleichen |
| Stärke | oft effizient bei kleiner Liste | Vorteil wächst mit der Listengröße |
Beispiel: Telefonbuch nach Namen geordnet
- 1Das Telefonbuch ist nach Namen geordnet.
- 2Suche nach einem bestimmten Namen: Binäre Suche ist möglich.
- 3Suche nach einer bestimmten Telefonnummer: Binäre Suche ist nicht möglich, denn die Ordnung bezieht sich auf die Namen.
- 4Ordnung und Suche müssen sich also auf denselben Schlüssel beziehen.
Teste dich
Alles auf einen Blick
Landau-Notation
Schranken
O ist die obere, Ω die untere und Θ die enge Schranke. Θ bedeutet O und Ω zugleich.
Vereinfachen
Bei einer Summe zählt nur der am schnellsten wachsende Summand, konstante Faktoren fallen weg.
Reihenfolge
log(n), n, n·log(n), n², n³ wachsen in dieser Reihenfolge schneller.
Fall und Schranke
Best, Average und Worst Case nennen die Eingabe. O, Ω und Θ nennen die Art der Schranke.
Vergleich
Prüfe neben dem Wachstum die Vorbedingungen, etwa sortierte Daten bei der binären Suche.
Musteraufgabe · Schritt für Schritt
Insertionsort: Best und Worst Case mit Schranken (aus den Kostenfunktionen hergeleitet)
- 1Gegeben sind die Vergleiche bei n Elementen: Tbest(n) = n − 1 und Tworst(n) = 1 + 2 + … + (n − 1).
- 2Probe mit n = 10: Tbest = 9 und Tworst = 1 + 2 + … + 9 = 45.
- 3Die Summe ist n·(n−1)/2 = n²/2 − n/2. Für n = 10 ergibt das 50 − 5 = 45.
- 4Best Case: (n − 1)/n strebt gegen 1, also gegen eine Konstante c > 0. Tbest wächst genauso schnell wie n, also Tbest ∈ Θ(n).
- 5Worst Case: (n²/2 − n/2)/n² strebt gegen 1/2. Tworst wächst genauso schnell wie n², also Tworst ∈ Θ(n²).
- 6Fall und Schranke sind getrennt: Für beide Fälle kannst du auch eine obere Schranke nennen, O(n) für den Best Case und O(n²) für den Worst Case.
Fehler finden
Prüfe die Aussagen zum Quicksort In einer Zeile steckt ein Fehler. Tippe sie an.
Fehler in Zeile 4Der Fall legt das Symbol nicht fest. Richtig: Auch für einen Best Case gibt man eine obere Schranke O an, zum Beispiel O(n) beim Insertionsort.
Lückentext
Wähl in jeder Lücke das passende Wort und prüf dann deine Antworten.
Die Aussage „f wächst mindestens genauso schnell wie g“ gehört zum Symbol Ω und damit zur Schranke. Bei O(n³ + n² + 1) zählt nur der Summand, deshalb bleibt O(n³) übrig. Die binäre Suche setzt voraus, dass die Elemente im Array angeordnet sind.
Karteikasten
Erst selbst überlegen, dann umdrehen.
Übung mit Feedback