Appearance
âïž Thema 6: Algorithmen â
Ăberblick â
In diesem Themenbereich geht es darum, wie Probleme mit klaren Schrittfolgen gelöst werden können. Solche Schrittfolgen nennt man Algorithmen.
Algorithmen begegnen uns nicht nur beim Programmieren, sondern auch im Alltag:
- beim Suchen eines Namens in einer Liste,
- beim Sortieren von Karten,
- beim Navigieren mit einer Karten-App,
- beim Filtern von Suchergebnissen,
- beim Empfehlen von Videos oder Musik,
- beim Planen von Routen,
- beim automatischen Auswerten von Daten.
In der Informatik interessiert nicht nur, ob ein Algorithmus funktioniert. Wichtig ist auch, wie effizient er arbeitet und wie sich sein Verhalten bei kleinen oder groĂen Datenmengen verĂ€ndert.
Leitfrage
Wie lassen sich Probleme durch klare Schrittfolgen lösen, und woran erkennt man, ob ein Algorithmus effizient arbeitet?
Was ist ein Algorithmus? â
Ein Algorithmus ist eine eindeutige Schrittfolge zur Lösung eines Problems.
Ein Algorithmus beschreibt also nicht nur ungefĂ€hr, was passieren soll, sondern legt fest, welche Schritte in welcher Reihenfolge ausgefĂŒhrt werden.
Beispiel aus dem Alltag:
Suche ein bestimmtes Wort in einem Wörterbuch.
Mögliche Strategie:
- Ăffne das Wörterbuch ungefĂ€hr in der Mitte.
- Vergleiche das gesuchte Wort mit den Wörtern auf der Seite.
- Entscheide, ob du links oder rechts weitersuchen musst.
- Wiederhole das Vorgehen, bis du das Wort gefunden hast oder sicher bist, dass es nicht vorkommt.
Merke
Ein Algorithmus ist eine prĂ€zise Vorschrift, die Schritt fĂŒr Schritt zu einer Lösung fĂŒhren soll.
Eigenschaften von Algorithmen â
Algorithmen besitzen typische Eigenschaften.
| Eigenschaft | Bedeutung |
|---|---|
| Eindeutigkeit | Jeder Schritt ist klar beschrieben. |
| AusfĂŒhrbarkeit | Jeder Schritt kann tatsĂ€chlich durchgefĂŒhrt werden. |
| Endlichkeit | Der Algorithmus endet nach endlich vielen Schritten. |
| Korrektheit | Der Algorithmus liefert fĂŒr gĂŒltige Eingaben das richtige Ergebnis. |
| Allgemeinheit | Der Algorithmus funktioniert nicht nur fĂŒr einen Einzelfall, sondern fĂŒr eine Klasse Ă€hnlicher Probleme. |
| Effizienz | Der Algorithmus löst das Problem mit möglichst angemessenem Aufwand. |
Wichtig
Ein Algorithmus kann korrekt sein, aber trotzdem unpraktisch langsam. Deshalb ist Effizienz ein wichtiger Bestandteil der Bewertung.
Determinismus und Determiniertheit â
Zwei Begriffe klingen Àhnlich, bedeuten aber nicht genau dasselbe.
| Begriff | Bedeutung |
|---|---|
| Determinismus | Bei jedem Schritt ist eindeutig festgelegt, welcher nÀchste Schritt folgt. |
| Determiniertheit | Bei gleicher Eingabe entsteht immer dieselbe Ausgabe. |
Ein deterministischer Algorithmus ist Schritt fĂŒr Schritt eindeutig festgelegt.
Ein determinierter Algorithmus liefert bei gleicher Eingabe immer dasselbe Ergebnis.
Merke
Determinismus beschreibt den Ablauf.
Determiniertheit beschreibt das Ergebnis.
Ein Beispiel fĂŒr einen nicht vollstĂ€ndig vorhersehbaren Ablauf wĂ€re ein Programm, das Zufallszahlen verwendet. Es kann trotzdem nach festen Regeln programmiert sein, aber die konkrete Ausgabe kann sich unterscheiden.
Effizienz â
Effizienz beschreibt, wie viele Ressourcen ein Algorithmus benötigt.
Wichtige Ressourcen sind:
- Zeit,
- Speicherplatz,
- Energie,
- Anzahl der Vergleiche,
- Anzahl der Rechenschritte,
- Anzahl der Zugriffe auf Daten.
In der Schule betrachten wir meistens die Laufzeit bzw. die Anzahl der notwendigen Schritte.
Merksatz
Ein effizienter Algorithmus löst ein Problem nicht nur richtig, sondern auch mit möglichst geringem Aufwand.
Laufzeitverhalten â
Das Laufzeitverhalten beschreibt, wie sich der Aufwand eines Algorithmus verĂ€ndert, wenn die Eingabe gröĂer wird.
Dabei fragt man zum Beispiel:
- Was passiert bei 10 Elementen?
- Was passiert bei 1 000 Elementen?
- Was passiert bei 1 000 000 Elementen?
Ein Algorithmus, der bei kleinen Datenmengen schnell genug ist, kann bei groĂen Datenmengen plötzlich ungeeignet werden.
Best Case, Worst Case und Average Case â
Das Laufzeitverhalten hĂ€ngt oft davon ab, wie gĂŒnstig oder ungĂŒnstig die Eingabe ist.
| Fall | Bedeutung |
|---|---|
| Best Case | gĂŒnstigster Fall |
| Worst Case | ungĂŒnstigster Fall |
| Average Case | durchschnittlich erwarteter Fall |
Beispiel: Suche in einer Liste
- Best Case: Das gesuchte Element steht ganz vorne.
- Worst Case: Das gesuchte Element steht ganz hinten oder kommt gar nicht vor.
- Average Case: Das gesuchte Element liegt irgendwo in der Liste.
Merke
Bei der Bewertung von Algorithmen sollte man nicht nur den gĂŒnstigsten Fall betrachten. Besonders wichtig ist oft der Worst Case, weil er zeigt, wie schlecht es höchstens laufen kann.
Lineare Suche â
Die lineare Suche prĂŒft eine Liste Element fĂŒr Element.
Beispiel:
txt
[4, 9, 12, 18, 23, 31]Gesucht wird 18.
Ablauf:
- PrĂŒfe
4â nicht gesucht. - PrĂŒfe
9â nicht gesucht. - PrĂŒfe
12â nicht gesucht. - PrĂŒfe
18â gefunden.
Die lineare Suche funktioniert auch dann, wenn die Liste nicht sortiert ist.
Merke
Lineare Suche ist einfach und zuverlĂ€ssig, benötigt aber im ungĂŒnstigsten Fall viele Vergleiche.
Lineare Suche in Python â
python
def enthaelt_wert(liste, suchwert):
for element in liste:
if element == suchwert:
return True
return False
zahlen = [4, 9, 12, 18, 23, 31]
print(enthaelt_wert(zahlen, 18))
print(enthaelt_wert(zahlen, 7))Die Funktion durchlĂ€uft die Liste von links nach rechts. Sobald der gesuchte Wert gefunden wurde, gibt sie True zurĂŒck. Wird der Wert nicht gefunden, gibt sie am Ende False zurĂŒck.
Laufzeit der linearen Suche â
Bei n Elementen gilt:
| Fall | Aufwand |
|---|---|
| Best Case | 1 Vergleich |
| Worst Case | n Vergleiche |
| Average Case | ungefÀhr n/2 Vergleiche |
Wichtig
Die lineare Suche braucht keine sortierte Liste. Das ist ein Vorteil. Bei groĂen Listen kann sie aber langsam werden.
đ Ăbung: Lineare Suche nachvollziehen â
Gegeben ist die Liste:
txt
[6, 11, 15, 22, 27, 34, 40]Gesucht wird 27.
- Notiere, welche Elemente der Reihe nach geprĂŒft werden.
- Gib an, nach wie vielen Vergleichen der Wert gefunden wird.
- ErklĂ€re, was passieren wĂŒrde, wenn
99gesucht wird.
Lösung
GeprĂŒft werden:
txt
6, 11, 15, 22, 27Der Wert wird nach 5 Vergleichen gefunden.
Wenn 99 gesucht wird, muss die ganze Liste durchsucht werden. Danach kann man feststellen, dass der Wert nicht vorkommt.
BinĂ€re Suche â
Die binÀre Suche ist deutlich effizienter als die lineare Suche, hat aber eine wichtige Voraussetzung:
Voraussetzung
BinÀre Suche funktioniert nur korrekt, wenn die Liste sortiert ist.
Die Grundidee:
- Betrachte das mittlere Element der Liste.
- Vergleiche den Suchwert mit diesem Element.
- Ist der Suchwert gleich, wurde er gefunden.
- Ist der Suchwert kleiner, suche nur links weiter.
- Ist der Suchwert gröĂer, suche nur rechts weiter.
- Wiederhole das Vorgehen.
Beispiel:
txt
[2, 5, 8, 12, 16, 23, 38, 45, 56]Gesucht wird 45.
Ablauf:
Mitte:
1645ist gröĂer â rechts weitersuchen.Neuer Bereich:
txt
[23, 38, 45, 56]Mitte ungefĂ€hr: 3845 ist gröĂer â rechts weitersuchen.
- Neuer Bereich:
txt
[45, 56]Mitte: 45
gefunden.
Merke
Bei jedem Schritt wird ungefÀhr die HÀlfte der verbleibenden Liste ausgeschlossen.
BinĂ€re Suche in Python â
python
def binaere_suche(sortierte_liste, suchwert):
links = 0
rechts = len(sortierte_liste) - 1
while links <= rechts:
mitte = (links + rechts) // 2
aktueller_wert = sortierte_liste[mitte]
if aktueller_wert == suchwert:
return True
elif suchwert < aktueller_wert:
rechts = mitte - 1
else:
links = mitte + 1
return False
werte = [2, 5, 8, 12, 16, 23, 38, 45, 56]
print(binaere_suche(werte, 45))
print(binaere_suche(werte, 7))Merksatz
BinĂ€re Suche ist besonders stark bei groĂen, sortierten Datenmengen.
Lineare und binĂ€re Suche vergleichen â
| Merkmal | Lineare Suche | BinÀre Suche |
|---|---|---|
| Voraussetzung | keine Sortierung nötig | Liste muss sortiert sein |
| Vorgehen | Element fĂŒr Element prĂŒfen | Suchbereich halbieren |
| Vorteil | einfach, funktioniert immer | sehr effizient bei groĂen Listen |
| Nachteil | langsam bei groĂen Listen | Sortierung muss vorhanden sein |
| Worst Case | n Vergleiche | ungefĂ€hr logâ(n) Vergleiche |
Beispielhafte GröĂenordnung:
| Anzahl Elemente | Lineare Suche im Worst Case | BinÀre Suche ungefÀhr |
|---|---|---|
| 10 | 10 Vergleiche | 4 Vergleiche |
| 100 | 100 Vergleiche | 7 Vergleiche |
| 1 000 | 1 000 Vergleiche | 10 Vergleiche |
| 1 000 000 | 1 000 000 Vergleiche | 20 Vergleiche |
Merke
Der Unterschied zwischen linearer und binĂ€rer Suche wird bei groĂen Datenmengen besonders deutlich.
Lohnt sich Sortieren vor dem Suchen? â
Ob sich Sortieren vor dem Suchen lohnt, hÀngt vom Anwendungsfall ab.
Wenn du nur einmal in einer kleinen unsortierten Liste suchst, ist Sortieren oft unnötiger Zusatzaufwand.
Wenn du aber sehr oft in derselben Liste suchst, kann es sinnvoll sein, die Liste zuerst zu sortieren und danach binÀr zu suchen.
Denkfrage
Sortieren kostet zuerst Aufwand. Dieser Aufwand kann sich lohnen, wenn danach viele SuchvorgÀnge schneller werden.
Beispiel:
Eine unsortierte Liste enthÀlt 20 Werte. Du suchst nur einmal.
- Lineare Suche ist einfach und wahrscheinlich ausreichend.
- Sortieren wÀre zusÀtzlicher Aufwand.
Eine Liste enthĂ€lt 100 000 Werte. Du musst tĂ€glich viele Suchanfragen durchfĂŒhren.
- Einmaliges Sortieren kann sinnvoll sein.
- Danach kann jede Suche mit binÀrer Suche deutlich schneller erfolgen.
Wichtig
Effizienz hĂ€ngt vom Kontext ab. Ein Algorithmus ist nicht immer âbesserâ, sondern nur fĂŒr bestimmte Bedingungen besser geeignet.
Sortieren â
Ein Sortieralgorithmus bringt Elemente in eine bestimmte Reihenfolge.
Beispiele:
- Zahlen aufsteigend sortieren,
- Namen alphabetisch sortieren,
- Dateien nach Datum sortieren,
- Produkte nach Preis sortieren,
- Nachrichten nach Uhrzeit sortieren.
Sortieren ist in der Informatik sehr wichtig, weil viele andere Verfahren dadurch schneller oder einfacher werden.
Merke
Sortieren braucht ein Sortierkriterium. Ohne erkennbares Kriterium sind Daten zwar vielleicht irgendwie angeordnet, aber informatisch nicht sinnvoll sortiert.
Selection Sort â
Selection Sort bedeutet âSortieren durch AuswĂ€hlenâ.
Die Grundidee:
- Suche im unsortierten Bereich das kleinste Element.
- Tausche es mit dem ersten Element des unsortierten Bereichs.
- Der sortierte Bereich wÀchst um ein Element.
- Wiederhole das Vorgehen, bis die Liste sortiert ist.
Beispiel:
txt
[6, 2, 9, 4]Erster Durchlauf:
- kleinstes Element suchen:
2 2mit dem ersten Element6tauschen- Ergebnis:
txt
[2, 6, 9, 4]Zweiter Durchlauf:
- unsortierter Bereich:
[6, 9, 4] - kleinstes Element:
4 4mit6tauschen- Ergebnis:
txt
[2, 4, 9, 6]Dritter Durchlauf:
- unsortierter Bereich:
[9, 6] - kleinstes Element:
6 6mit9tauschen- Ergebnis:
txt
[2, 4, 6, 9]Merke
Bei Selection Sort wird in jedem Durchlauf das kleinste Element des unsortierten Bereichs ausgewÀhlt und an die nÀchste richtige Position gebracht.
Vertiefung: Insertion Sort â
Insertion Sort bedeutet âSortieren durch EinfĂŒgenâ.
Die Grundidee Àhnelt dem Sortieren von Spielkarten auf der Hand:
- Ein Teil der Liste gilt bereits als sortiert.
- Das nÀchste Element aus dem unsortierten Bereich wird genommen.
- Dieses Element wird an der passenden Stelle in den sortierten Bereich eingefĂŒgt.
- Der sortierte Bereich wĂ€chst Schritt fĂŒr Schritt.
Beispiel:
txt
[5, 2, 8, 1]Man betrachtet zunÀchst 5 als sortierten Bereich.
Dann wird 2 passend eingefĂŒgt:
txt
[2, 5, 8, 1]Dann wird 8 eingefĂŒgt. Es bleibt hinter 5:
txt
[2, 5, 8, 1]Dann wird 1 ganz vorne eingefĂŒgt:
txt
[1, 2, 5, 8]Merke
Bei Insertion Sort wird jedes neue Element in einen bereits sortierten Bereich passend eingefĂŒgt.
Bubble Sort â
Bubble Sort ist ein einfacher Sortieralgorithmus. Er vergleicht immer benachbarte Elemente und vertauscht sie, wenn sie in der falschen Reihenfolge stehen.
Beispiel:
txt
[5, 2, 8, 1]Erster Durchlauf:
- Vergleiche
5und2â vertauschen:[2, 5, 8, 1] - Vergleiche
5und8â nichts tun:[2, 5, 8, 1] - Vergleiche
8und1â vertauschen:[2, 5, 1, 8]
Nach einem vollstĂ€ndigen Durchlauf steht das gröĂte Element am Ende.
Merke
Bei Bubble Sort âwandernâ groĂe Werte Schritt fĂŒr Schritt nach rechts.
Bubble Sort in Python â
python
def bubble_sort(liste):
n = len(liste)
for durchlauf in range(n):
for i in range(0, n - 1 - durchlauf):
if liste[i] > liste[i + 1]:
liste[i], liste[i + 1] = liste[i + 1], liste[i]
return liste
werte = [5, 2, 8, 1]
print(bubble_sort(werte))Bubble Sort ist leicht verstĂ€ndlich, aber bei groĂen Listen meist nicht effizient.
Bubble Sort mit Abbruch â
Eine optimierte Variante merkt sich, ob in einem Durchlauf ĂŒberhaupt vertauscht wurde. Wenn nichts vertauscht wurde, ist die Liste bereits sortiert.
python
def bubble_sort_mit_abbruch(liste):
n = len(liste)
for durchlauf in range(n):
getauscht = False
for i in range(0, n - 1 - durchlauf):
if liste[i] > liste[i + 1]:
liste[i], liste[i + 1] = liste[i + 1], liste[i]
getauscht = True
if not getauscht:
break
return liste
werte = [1, 2, 3, 4, 5]
print(bubble_sort_mit_abbruch(werte))Merksatz
Der Abbruch spart Zeit, wenn die Liste bereits sortiert ist oder frĂŒh sortiert wird.
Code-Tracing bei Sortieralgorithmen â
Beim Code-Tracing wird ein Programm Schritt fĂŒr Schritt nachvollzogen. Dabei notiert man, wie sich Variablen und Listen verĂ€ndern.
Beispiel:
python
werte = [4, 1, 3]
for i in range(len(werte) - 1):
if werte[i] > werte[i + 1]:
werte[i], werte[i + 1] = werte[i + 1], werte[i]Ablauf:
| i | Vergleich | Tausch? | Liste danach |
|---|---|---|---|
| 0 | 4 > 1 | ja | [1, 4, 3] |
| 1 | 4 > 3 | ja | [1, 3, 4] |
Merke
Code-Tracing zeigt, wie ein Algorithmus tatsÀchlich arbeitet. Das ist besonders hilfreich bei Schleifen, Bedingungen und ListenverÀnderungen.
đ Ăbung: Code-Tracing â
Gegeben ist der Code:
python
zahlen = [7, 3, 5]
for i in range(len(zahlen) - 1):
if zahlen[i] > zahlen[i + 1]:
zahlen[i], zahlen[i + 1] = zahlen[i + 1], zahlen[i]
print(zahlen)- Notiere fĂŒr jeden Schleifendurchlauf den Wert von
i. - Notiere, welche zwei Werte verglichen werden.
- Notiere, ob getauscht wird.
- Gib die Liste nach jedem Durchlauf an.
Lösung
Start:
txt
[7, 3, 5]| i | Vergleich | Tausch? | Liste danach |
|---|---|---|---|
| 0 | 7 > 3 | ja | [3, 7, 5] |
| 1 | 7 > 5 | ja | [3, 5, 7] |
Ausgabe:
txt
[3, 5, 7]Vertiefung: Rekursion â
Rekursion bedeutet, dass eine Funktion sich selbst aufruft.
Ein rekursiver Algorithmus braucht immer:
- einen Rekursionsanker, also einen Fall, in dem die Rekursion endet,
- einen Rekursionsschritt, bei dem das Problem verkleinert und erneut bearbeitet wird.
Ein einfaches Beispiel ist die Summe aller Zahlen von n bis 1.
python
def summe_bis(n):
if n <= 0:
return 0
else:
return n + summe_bis(n - 1)FĂŒr summe_bis(4) entsteht gedanklich:
txt
4 + 3 + 2 + 1Wichtig
Ohne Rekursionsanker wĂŒrde sich eine Funktion immer weiter selbst aufrufen. Dann entsteht eine endlose Rekursion.
Quicksort â
Quicksort ist ein schneller Sortieralgorithmus, der rekursiv arbeitet. Er folgt dem Prinzip Teile und herrsche.
Die Grundidee:
- WĂ€hle ein Element als Vergleichswert. Dieses Element nennt man Pivot.
- Teile die ĂŒbrigen Elemente auf:
- kleinere Werte links,
- gröĂere Werte rechts.
- Sortiere die linke und rechte Teilliste wieder auf dieselbe Weise.
- Setze die Teile zusammen.
Beispiel:
txt
[8, 3, 10, 1, 6]WĂ€hle 8 als Pivot.
Kleinere Werte:
txt
[3, 1, 6]GröĂere Werte:
txt
[10]Dann werden die Teillisten weiter sortiert.
Merke
Quicksort zerlegt ein groĂes Sortierproblem in kleinere Sortierprobleme.
Quicksort in Python â
python
def quicksort(liste):
if len(liste) <= 1:
return liste
pivot = liste[0]
kleiner = []
groesser = []
for element in liste[1:]:
if element <= pivot:
kleiner.append(element)
else:
groesser.append(element)
return quicksort(kleiner) + [pivot] + quicksort(groesser)
werte = [8, 3, 10, 1, 6]
print(quicksort(werte))Quicksort ist in der Praxis oft sehr schnell. Der ungĂŒnstige Fall kann aber auftreten, wenn das Pivot-Element schlecht gewĂ€hlt wird.
Sortieralgorithmen vergleichen â
| Algorithmus | Grundidee | Vorteil | Grenze |
|---|---|---|---|
| Selection Sort | kleinstes Element suchen und nach vorne tauschen | leicht verstÀndlich | viele Vergleiche |
| Insertion Sort | Elemente in sortierten Bereich einfĂŒgen | gut bei fast sortierten Daten | bei groĂen unsortierten Listen langsam |
| Bubble Sort | benachbarte Elemente vergleichen und tauschen | sehr gut sichtbar und nachvollziehbar | bei groĂen Listen ineffizient |
| Quicksort | Pivot wÀhlen und rekursiv aufteilen | im Durchschnitt sehr effizient | im Worst Case quadratisch |
Wichtig
Ein Algorithmus kann didaktisch gut geeignet sein, obwohl er praktisch nicht der effizienteste ist. Bubble Sort ist leicht zu verstehen, wird aber fĂŒr groĂe Datenmengen selten bevorzugt.
KomplexitĂ€tsklassen â
Bei Algorithmen interessiert nicht nur, wie viele Schritte sie bei einer bestimmten Liste brauchen. Wichtiger ist oft die Frage:
Wie wĂ€chst der Aufwand, wenn die Datenmenge gröĂer wird?
DafĂŒr verwendet man sogenannte KomplexitĂ€tsklassen. Sie beschreiben das Wachstum des Aufwands in AbhĂ€ngigkeit von der ProblemgröĂe n.
Typische KomplexitÀtsklassen sind:
| Schreibweise | Bezeichnung | Bedeutung |
|---|---|---|
O(1) | konstant | Der Aufwand bleibt ungefÀhr gleich, egal wie groà n ist. |
O(log n) | logarithmisch | Der Aufwand wÀchst sehr langsam. |
O(n) | linear | Der Aufwand wÀchst ungefÀhr proportional zur Datenmenge. |
O(n log n) | linearithmisch / quasilinear | Der Aufwand wÀchst schneller als linear, aber deutlich langsamer als quadratisch. |
O(nÂČ) | quadratisch | Der Aufwand wĂ€chst stark, weil hĂ€ufig verschachtelte Vergleiche nötig sind. |
O(2âż) | exponentiell | Der Aufwand wĂ€chst extrem stark mit jedem zusĂ€tzlichen Element. |
O(n!) | faktoriell | Der Aufwand wÀchst noch stÀrker; typisch bei vollstÀndigem Durchprobieren vieler Anordnungen. |
Merke
KomplexitĂ€tsklassen beschreiben nicht die exakte Laufzeit in Sekunden, sondern das Wachstum des Aufwands bei gröĂer werdenden Datenmengen.
Beispiele fĂŒr KomplexitĂ€tsklassen â
Konstanter Aufwand: O(1) â
Der Aufwand bleibt ungefÀhr gleich, egal wie groà die Datenmenge ist.
Beispiel:
python
erstes_element = liste[0]Der Zugriff auf ein bestimmtes Listenelement ĂŒber seinen Index benötigt nicht mehr Schritte, nur weil die Liste gröĂer wird.
Logarithmischer Aufwand: O(log n) â
Der Aufwand wÀchst sehr langsam.
Beispiel:
txt
BinÀre SucheBei jedem Schritt wird der Suchbereich halbiert.
txt
1 000 Elemente â ungefĂ€hr 10 Vergleiche
1 000 000 Elemente â ungefĂ€hr 20 VergleicheMerke
O(log n) ist sehr effizient, weil bei jedem Schritt ein groĂer Teil des Problems ausgeschlossen wird.
Linearer Aufwand: O(n) â
Der Aufwand wÀchst ungefÀhr proportional zur Anzahl der Elemente.
Beispiel:
txt
Lineare SucheIm schlechtesten Fall wird jedes Element einmal geprĂŒft.
txt
10 Elemente â bis zu 10 Vergleiche
100 Elemente â bis zu 100 Vergleiche
10 000 Elemente â bis zu 10 000 VergleicheLinearithmischer Aufwand: O(n log n) â
Der Aufwand wÀchst etwas schneller als linear, aber deutlich langsamer als quadratisch.
Typische Beispiele:
- Quicksort im Durchschnitt,
- Mergesort,
- Heapsort.
Bei solchen Verfahren wird die Datenmenge oft sinnvoll in Teilprobleme zerlegt, die anschlieĂend effizient bearbeitet werden.
Quadratischer Aufwand: O(nÂČ) â
Der Aufwand wÀchst stark, weil hÀufig viele Elemente mit vielen anderen Elementen verglichen werden.
Typische Beispiele:
- Selection Sort,
- Bubble Sort,
- Insertion Sort im durchschnittlichen und schlechten Fall.
txt
10 Elemente â ungefĂ€hr 100 als GröĂenordnung
100 Elemente â ungefĂ€hr 10 000 als GröĂenordnung
10 000 Elemente â ungefĂ€hr 100 000 000 als GröĂenordnungWichtig
Quadratische Algorithmen können bei kleinen Datenmengen völlig ausreichend sein, werden aber bei groĂen Datenmengen schnell problematisch.
KomplexitĂ€t von Such- und Sortierverfahren â
| Verfahren | Best Case | Average Case | Worst Case | KurzbegrĂŒndung |
|---|---|---|---|---|
| Lineare Suche | O(1) | O(n) | O(n) | Im besten Fall steht das Element vorne, sonst muss viel geprĂŒft werden. |
| BinÀre Suche | O(1) | O(log n) | O(log n) | Der Suchbereich wird immer halbiert. |
| Selection Sort | O(nÂČ) | O(nÂČ) | O(nÂČ) | Das kleinste Element muss wiederholt im Restbereich gesucht werden. |
| Bubble Sort | O(n) mit Abbruch | O(nÂČ) | O(nÂČ) | Bei bereits sortierter Liste kann frĂŒh abgebrochen werden; sonst viele Vergleiche. |
| Insertion Sort | O(n) | O(nÂČ) | O(nÂČ) | Bei fast sortierten Daten gĂŒnstig, sonst viele Verschiebungen. |
| Quicksort | O(n log n) | O(n log n) | O(nÂČ) | Gute Pivot-Aufteilung ist effizient; schlechte Pivot-Wahl fĂŒhrt zu ungĂŒnstiger Rekursion. |
Strategie
Wenn du KomplexitÀtsklassen erklÀrst, nenne immer auch den Kontext:
- Geht es um Suchen oder Sortieren?
- Ist die Liste sortiert oder unsortiert?
- Wird einmal gesucht oder sehr oft?
- Geht es um kleine oder groĂe Datenmengen?
- Wird der Best Case, Worst Case oder Average Case betrachtet?
Quicksort und O(n log n) â
Quicksort arbeitet im Durchschnitt sehr effizient, weil die Liste durch das Pivot-Element in kleinere Teillisten aufgeteilt wird.
Wenn das Pivot-Element die Liste ungefĂ€hr halbiert, entstehen ungefĂ€hr logâ(n) Rekursionsebenen. Auf jeder Ebene mĂŒssen die Elemente mit dem Pivot verglichen werden.
Daher ergibt sich im gĂŒnstigen und durchschnittlichen Fall ungefĂ€hr:
txt
n · logâ(n)also:
txt
O(n log n)Im ungĂŒnstigen Fall wird das Pivot-Element aber sehr schlecht gewĂ€hlt. Wenn es immer das kleinste oder gröĂte Element ist, entsteht jedes Mal eine fast gleich groĂe Restliste. Dann Ă€hnelt der Aufwand wieder einer quadratischen Struktur:
txt
O(nÂČ)Merke
Quicksort ist im Durchschnitt sehr schnell, kann aber im schlechtesten Fall deutlich ineffizienter werden.
Vergleich von n log n und nÂČ â
Der Unterschied zwischen O(n log n) und O(nÂČ) wird bei groĂen Datenmengen besonders deutlich. Bei kleinen Datenmengen können konstante Faktoren und Implementierungsdetails den praktischen Vergleich zunĂ€chst ĂŒberlagern.
Beispielhaft:
ProblemgröĂe n | n logâ(n) ungefĂ€hr | nÂČ |
|---|---|---|
| 10 | 33 | 100 |
| 100 | 664 | 10 000 |
| 1 000 | 9 966 | 1 000 000 |
| 10 000 | 132 877 | 100 000 000 |
Wichtig
Bei kleinen Datenmengen wirken Sortierverfahren oft Ă€hnlich schnell. Bei groĂen Datenmengen entscheiden KomplexitĂ€tsklassen darĂŒber, ob ein Verfahren praktisch sinnvoll ist.
Elementaroperationen beim Sortieren â
Beim Sortieren kann man verschiedene Arten von Arbeitsschritten zÀhlen.
Wichtige Elementaroperationen sind:
| Operation | Bedeutung |
|---|---|
| Vergleich | Zwei Werte werden verglichen. |
| Verschiebung | Ein Wert wird an eine andere Stelle verschoben. |
| Vertauschung | Zwei Werte tauschen ihre PlÀtze. |
Eine Vertauschung ist aufwendiger als eine einfache Verschiebung, weil dafĂŒr mehrere Zuweisungen nötig sind.
Beispiel fĂŒr eine Vertauschung:
python
zwischenspeicher = liste[i]
liste[i] = liste[i + 1]
liste[i + 1] = zwischenspeicherIn Python kann man das kĂŒrzer schreiben:
python
liste[i], liste[i + 1] = liste[i + 1], liste[i]Merke
Effizienz kann man nicht nur ĂŒber die Anzahl der SchleifendurchlĂ€ufe betrachten. Auch Vergleiche, Verschiebungen und Vertauschungen verursachen Aufwand.
Sortieren mit Karten verstehen â
Sortieralgorithmen lassen sich gut mit Karten erklÀren.
FĂŒr Selection Sort:
- Lege mehrere Zahlenkarten nebeneinander.
- Suche das kleinste Element im unsortierten Bereich.
- Tausche es nach vorne.
- Markiere den sortierten Bereich.
- Wiederhole das Vorgehen.
FĂŒr Insertion Sort:
- Betrachte die erste Karte als sortierten Bereich.
- Nimm die nÀchste Karte.
- FĂŒge sie an der richtigen Stelle in den sortierten Bereich ein.
- Wiederhole das Vorgehen.
FĂŒr Bubble Sort:
- Vergleiche immer zwei benachbarte Karten.
- Vertausche sie, wenn sie falsch liegen.
- Gehe bis zum Ende der Reihe.
- Wiederhole den Vorgang, bis keine Vertauschung mehr nötig ist.
FĂŒr Quicksort:
- WĂ€hle eine Karte als Pivot.
- Lege kleinere Werte links ab.
- Lege gröĂere Werte rechts ab.
- Wiederhole das Vorgehen fĂŒr die Teilgruppen.
- Setze die sortierten Gruppen wieder zusammen.
Strategie
Wenn du einen Algorithmus mit Karten erklÀrst, sprich jeden Schritt laut mit: Was wird verglichen? Warum wird getauscht? Welcher Bereich ist bereits erledigt?
Algorithmen bewerten â
Beim Vergleichen von Algorithmen helfen Leitfragen:
- Funktioniert der Algorithmus fĂŒr alle gĂŒltigen Eingaben?
- Welche Voraussetzungen braucht er?
- Wie viele Schritte benötigt er ungefÀhr?
- Wie verhÀlt er sich im Best Case, Worst Case und Average Case?
- Zu welcher KomplexitÀtsklasse gehört er?
- Ist er leicht zu verstehen?
- Ist er leicht zu programmieren?
- Ist er fĂŒr groĂe Datenmengen geeignet?
- Muss vorher sortiert oder vorbereitet werden?
- Gibt es einen Zielkonflikt zwischen Effizienz und VerstÀndlichkeit?
Merke
Die Wahl eines Algorithmus hÀngt vom Problem, von der Datenmenge, von den Voraussetzungen und vom praktischen Einsatz ab.
Effizienz kritisch reflektieren â
Effizienz ist nicht nur eine technische Frage.
Wenn Programme mit sehr groĂen Datenmengen arbeiten, können ineffiziente Algorithmen Folgen haben:
- lÀngere Wartezeiten,
- höhere Serverlast,
- mehr Energieverbrauch,
- höhere Kosten,
- schlechtere Nutzbarkeit,
- langsamere Auswertung wichtiger Daten,
- ökologische Auswirkungen durch höheren Ressourcenverbrauch.
Gleichzeitig ist der effizienteste Algorithmus nicht immer automatisch die beste Wahl.
Manchmal sind andere Kriterien wichtig:
- VerstÀndlichkeit,
- Wartbarkeit,
- Nachvollziehbarkeit,
- FehleranfÀlligkeit,
- Fairness,
- Transparenz.
Wichtig
Ein sehr effizienter Algorithmus kann schwer verstĂ€ndlich sein. Ein einfacher Algorithmus kann leichter ĂŒberprĂŒfbar sein. In der Praxis muss man oft zwischen Effizienz und Nachvollziehbarkeit abwĂ€gen.
Typische MissverstĂ€ndnisse â
âWenn ein Algorithmus funktioniert, ist er automatisch gut.â â
Nicht unbedingt. Er kann korrekt sein, aber bei groĂen Datenmengen viel zu langsam werden.
âBinĂ€re Suche ist immer besser als lineare Suche.â â
Nicht immer. BinÀre Suche braucht eine sortierte Liste. Bei sehr kleinen oder einmaligen SuchvorgÀngen kann lineare Suche einfacher und ausreichend sein.
âSortieren lohnt sich immer.â â
Nein. Sortieren kostet selbst Aufwand. Es lohnt sich vor allem, wenn danach oft gesucht oder weiterverarbeitet wird.
âBubble Sort ist schlecht, weil er langsam ist.â â
Bubble Sort ist fĂŒr groĂe Listen ineffizient, aber didaktisch sehr nĂŒtzlich, weil man daran Vergleiche, Vertauschungen und Schleifen gut versteht.
âQuicksort ist immer schnell.â â
Quicksort ist oft schnell, kann aber im ungĂŒnstigen Fall langsamer werden, wenn die Aufteilung sehr ungleich ist.
âKomplexitĂ€tsklassen geben die genaue Laufzeit in Sekunden an.â â
Nein. KomplexitÀtsklassen beschreiben das Wachstum des Aufwands, nicht die konkrete Laufzeit auf einem bestimmten Computer.
PrĂŒfungsvorbereitung â
Die folgenden Aufgaben trainieren dieselben Kompetenzen, verwenden aber andere Kontexte, Daten und Beispiele.
đ Ăbung: Algorithmus erklĂ€ren â
ErklÀre, warum die folgende Handlung als Algorithmus beschrieben werden kann:
Eine App prĂŒft, ob ein eingegebener Rabattcode in einer Liste gĂŒltiger Codes vorkommt.
Gehe dabei auf folgende Punkte ein:
- Eingabe
- Verarbeitung
- Ausgabe
- Eindeutigkeit der Schritte
- Endlichkeit
Lösungshinweis
Die Eingabe ist der Rabattcode. Die Verarbeitung besteht darin, den Code mit gespeicherten gĂŒltigen Codes zu vergleichen. Die Ausgabe ist zum Beispiel gĂŒltig oder ungĂŒltig.
Die Schritte mĂŒssen eindeutig sein, damit klar ist, welcher Code wann geprĂŒft wird. Der Algorithmus ist endlich, wenn die Liste nach einer bestimmten Anzahl von Vergleichen vollstĂ€ndig geprĂŒft wurde.
đ Ăbung: Lineare Suche implementieren â
Schreibe eine Funktion finde_artikel(liste, artikelnummer), die ĂŒberprĂŒft, ob eine Artikelnummer in einer Liste vorkommt.
Die Funktion soll True zurĂŒckgeben, wenn die Nummer gefunden wurde, sonst False.
Lösung
python
def finde_artikel(liste, artikelnummer):
for nummer in liste:
if nummer == artikelnummer:
return True
return False
artikel = [104, 118, 203, 260, 315]
print(finde_artikel(artikel, 203))
print(finde_artikel(artikel, 999))đ Ăbung: BinĂ€re Suche gedanklich durchfĂŒhren â
Gegeben ist die sortierte Liste:
txt
[3, 7, 12, 18, 25, 31, 44, 58, 63]Gesucht wird 44.
- Bestimme das mittlere Element.
- Entscheide, ob links oder rechts weitergesucht wird.
- Wiederhole den Vorgang, bis der Wert gefunden ist.
- Notiere alle verglichenen Werte.
Lösung
Startliste:
txt
[3, 7, 12, 18, 25, 31, 44, 58, 63]Mitte: 2544 ist gröĂer, also rechts weitersuchen.
Neuer Bereich:
txt
[31, 44, 58, 63]Mitte ungefÀhr: 44
gefunden.
Verglichene Werte:
txt
25, 44đ Ăbung: Sortieren oder nicht? â
Eine App verwaltet 30 gespeicherte Lieblingssongs. Eine andere App verwaltet 2 Millionen DatensÀtze.
- ErklÀre, warum lineare Suche bei 30 Songs oft ausreicht.
- ErklÀre, warum bei 2 Millionen DatensÀtzen andere Strategien sinnvoll werden.
- BegrĂŒnde, wann Sortieren vor dem Suchen sinnvoll sein kann.
Lösungshinweis
Bei 30 Songs ist der Aufwand gering. Selbst wenn alle EintrĂ€ge geprĂŒft werden, ist das meistens unproblematisch.
Bei 2 Millionen DatensÀtzen kann lineare Suche sehr viele Vergleiche benötigen. Wenn oft gesucht wird, lohnt sich eine sortierte Struktur oder ein anderer effizienter Zugriff.
Sortieren lohnt sich besonders, wenn dieselbe Datenmenge hÀufig durchsucht wird.
đ Ăbung: Selection Sort nachvollziehen â
Sortiere die Liste gedanklich mit Selection Sort:
txt
[7, 2, 6, 4]Notiere nach jedem Durchlauf den Zustand der Liste.
Lösung
Start:
txt
[7, 2, 6, 4]Erster Durchlauf: kleinstes Element ist 2, Tausch mit 7.
txt
[2, 7, 6, 4]Zweiter Durchlauf: kleinstes Element im Restbereich ist 4, Tausch mit 7.
txt
[2, 4, 6, 7]Dritter Durchlauf: 6 ist bereits an der richtigen Stelle.
txt
[2, 4, 6, 7]đ Ăbung: Vertiefung â Insertion Sort nachvollziehen â
Sortiere die Liste gedanklich mit Insertion Sort:
txt
[5, 3, 8, 2]Beschreibe, wie der sortierte Bereich Schritt fĂŒr Schritt wĂ€chst.
Lösung
Start:
txt
[5, 3, 8, 2]5 gilt als sortierter Bereich.
3 wird vor 5 eingefĂŒgt:
txt
[3, 5, 8, 2]8 bleibt hinter 5:
txt
[3, 5, 8, 2]2 wird ganz vorne eingefĂŒgt:
txt
[2, 3, 5, 8]đ Ăbung: Bubble Sort nachvollziehen â
Sortiere die Liste gedanklich mit Bubble Sort:
txt
[6, 2, 9, 4]Notiere nach jedem vollstÀndigen Durchlauf den Zustand der Liste.
Lösung
Start:
txt
[6, 2, 9, 4]Erster Durchlauf:
6und2tauschen â[2, 6, 9, 4]6und9bleiben â[2, 6, 9, 4]9und4tauschen â[2, 6, 4, 9]
Zweiter Durchlauf:
2und6bleiben â[2, 6, 4, 9]6und4tauschen â[2, 4, 6, 9]
Dritter Durchlauf:
- keine Ănderung nötig
Sortierte Liste:
txt
[2, 4, 6, 9]đ Ăbung: Quicksort-Prinzip erklĂ€ren â
Gegeben ist die Liste:
txt
[9, 4, 12, 2, 7]WĂ€hle 9 als Pivot.
- Teile die ĂŒbrigen Werte in kleinere und gröĂere Werte auf.
- ErklÀre, wie die Teillisten weiter sortiert werden.
- Beschreibe das Prinzip âTeile und herrscheâ.
Lösung
Pivot:
txt
9Kleinere Werte:
txt
[4, 2, 7]GröĂere Werte:
txt
[12]Die Teilliste [4, 2, 7] wird wieder nach demselben Prinzip sortiert. Das Problem wird also in kleinere Teilprobleme zerlegt. Danach werden die sortierten Teile zusammengesetzt.
đ Ăbung: KomplexitĂ€tsklassen zuordnen â
Ordne die folgenden Verfahren einer passenden KomplexitĂ€tsklasse zu und begrĂŒnde kurz:
- Lineare Suche in einer unsortierten Liste
- BinÀre Suche in einer sortierten Liste
- Bubble Sort
- Selection Sort
- Vertiefung: Insertion Sort im durchschnittlichen Fall
- Quicksort im durchschnittlichen Fall
- Quicksort im ungĂŒnstigen Fall
Lösung
- Lineare Suche:
O(n), weil im schlechtesten Fall jedes Element geprĂŒft wird. - BinĂ€re Suche:
O(log n), weil der Suchbereich in jedem Schritt halbiert wird. - Bubble Sort:
O(nÂČ), weil viele benachbarte Vergleiche und Vertauschungen nötig sind. - Selection Sort:
O(nÂČ), weil wiederholt das kleinste Element im unsortierten Bereich gesucht wird. - Vertiefung: Insertion Sort liegt im Durchschnitt bei
O(nÂČ), weil hĂ€ufig viele Verschiebungen nötig sind. - Quicksort im Durchschnitt:
O(n log n), weil die Liste meist sinnvoll aufgeteilt wird. - Quicksort im ungĂŒnstigen Fall:
O(nÂČ), wenn das Pivot-Element die Liste sehr schlecht aufteilt.
đ Ăbung: Wachstum vergleichen â
ErklĂ€re, warum ein Algorithmus mit O(n log n) bei groĂen Datenmengen meist deutlich besser geeignet ist als ein Algorithmus mit O(nÂČ).
Lösungshinweis
Bei O(nÂČ) wĂ€chst der Aufwand quadratisch. Verdoppelt sich die Datenmenge, kann sich der Aufwand ungefĂ€hr vervierfachen.
Bei O(n log n) wĂ€chst der Aufwand deutlich langsamer. Deshalb sind Verfahren wie Quicksort im durchschnittlichen Fall fĂŒr groĂe Datenmengen meist wesentlich geeigneter als einfache quadratische Sortierverfahren.
Ich kann ⊠â
Nach der Wiederholung dieses Themenbereichs solltest du Folgendes können:
- Ich kann erklÀren, was ein Algorithmus ist.
- Ich kann grundlegende Eigenschaften von Algorithmen nennen und beschreiben.
- Ich kann Determinismus und Determiniertheit unterscheiden.
- Ich kann erklÀren, was Effizienz bei Algorithmen bedeutet.
- Ich kann das Laufzeitverhalten eines Algorithmus beschreiben.
- Ich kann Best Case, Worst Case und Average Case unterscheiden.
- Ich kann die lineare Suche erklĂ€ren und an einem Beispiel durchfĂŒhren.
- Ich kann die binÀre Suche erklÀren und ihre Voraussetzung nennen.
- Ich kann lineare und binÀre Suche vergleichen.
- Ich kann beurteilen, wann Sortieren vor dem Suchen sinnvoll ist.
- Ich kann einfache Suchalgorithmen in Python lesen und schreiben.
- Ich kann Selection Sort Schritt fĂŒr Schritt erklĂ€ren.
- Ich kann Bubble Sort Schritt fĂŒr Schritt erklĂ€ren.
- Ich kann Quicksort als rekursives Teile-und-herrsche-Verfahren beschreiben.
- Ich kann einfache Sortieralgorithmen mit Karten oder Listen demonstrieren.
- Ich kann Code-Tracing bei Schleifen, Bedingungen und Listen durchfĂŒhren.
- Ich kann KomplexitÀtsklassen wie
O(log n),O(n),O(n log n)undO(nÂČ)unterscheiden. - Ich kann typische Laufzeitklassen von Such- und Sortierverfahren zuordnen.
- Ich kann begrĂŒnden, warum
O(n log n)bei groĂen Datenmengen meist gĂŒnstiger ist alsO(nÂČ). - Ich kann Algorithmen hinsichtlich VerstĂ€ndlichkeit, Korrektheit und Effizienz bewerten.
- Ich kann erklÀren, warum der passende Algorithmus vom konkreten Anwendungsfall abhÀngt.
- Ich kann gesellschaftliche, wirtschaftliche und ökologische Folgen ineffizienter Algorithmen reflektieren.
Mini-Check â
Beantworte zum Abschluss kurz:
- Welche Eigenschaften sollte ein Algorithmus besitzen?
- Was ist der Unterschied zwischen Determinismus und Determiniertheit?
- Wie funktioniert lineare Suche grundsÀtzlich?
- Welche Voraussetzung braucht binÀre Suche?
- Warum ist binĂ€re Suche bei groĂen sortierten Listen effizienter als lineare Suche?
- Was bedeuten best case, average case und worst case?
- Wie funktioniert Bubble Sort grundsÀtzlich?
- Wie funktioniert Quicksort grundsÀtzlich?
- Warum kann die Wahl des Pivot-Elements bei Quicksort wichtig sein?
- Welche KomplexitÀtsklasse passt typischerweise zu Bubble Sort im schlechten Fall?
- Welche KomplexitÀtsklasse passt typischerweise zu einem guten Quicksort-Verlauf?
- Wann kann es sinnvoll sein, vor dem Suchen zuerst zu sortieren?
- Warum ist Effizienz auch gesellschaftlich oder ökologisch relevant?
Kurzlösungen
- Er sollte eindeutig, ausfĂŒhrbar, endlich und nachvollziehbar sein und Eingaben zu Ausgaben verarbeiten.
- Determinismus betrifft die Eindeutigkeit jedes nÀchsten Schritts; Determiniertheit bedeutet, dass bei gleicher Eingabe dasselbe Ergebnis entsteht.
- Die Liste wird Element fĂŒr Element durchsucht, bis der Wert gefunden wird oder die Liste endet.
- Die Liste muss sortiert sein.
- Der Suchbereich wird in jedem Schritt ungefÀhr halbiert.
- Best case: gĂŒnstigster Fall; average case: durchschnittlicher Fall; worst case: ungĂŒnstigster Fall.
- Benachbarte Elemente werden verglichen und bei falscher Reihenfolge vertauscht; groĂe Elemente wandern schrittweise nach hinten.
- Ein Pivot wird gewĂ€hlt, die Liste wird in kleinere und gröĂere Elemente geteilt und diese Teile werden weiter sortiert.
- Ein ungĂŒnstiges Pivot kann sehr ungleich groĂe Teilprobleme erzeugen und die Laufzeit verschlechtern.
O(nÂČ).O(n log n).- Bei vielen Suchanfragen auf derselben Datenmenge, besonders wenn die Datenmenge groĂ ist.
- Langsame oder verschwenderische Algorithmen kosten Zeit, Energie und Ressourcen und können digitale Systeme unzuverlÀssig machen.