**Dies ist eine alte Version des Dokuments!**
Algorithmen II: Suchen & Sortieren
1. Suchen
Das Ziel einer Suche ist es, in einer grossen Datenmenge möglichst schnell das gesuchte Element zu finden. Beispielsweise suchen wir in einem Lexikon oder Wörterbuch den Eintrag zu einem Begriff, im Telefonbuch die Nummer einer Person oder eines Geschäfts. Heutzutage suchen wir zumeist im Internet - aber wie schafft es dict.leo.org einen Suchbegriff innert Millisekunden zu finden? Wie findet tel.search.ch den richtigen Eintrag?
Aufgabe A1: Suchen in einem Wörterbuch / Lexikon
Zu zweit oder dritt: Eine Person wählt in einem Wörterbuch oder Lexikon heimlich einen Eintrag, der auf einer Seite rechts oben steht. Das Buch wird geschlossen und eine andere Person versucht mit möglichst wenigen Suchschritten (1x Blättern ist ein Schritt), den Begriff zu finden. Es ist dabei verboten, auf die Markierungen mit den Anfangsbuchstaben an der Seite des Buches zu schauen.
Überlegt euch verschiedene Strategien, wie ihr vorgehen könnt. Welche Strategie benötigt die wenigsten Schritte?
1.1 Lineare Suche
Der einfachste Algorithmus ist die lineare Suche sucht das ganze Buch von vorne nach hinten durch. Dabei wird jedes Element solange mit dem gesuchten verglichen, bis man es gefunden hat.
Aufgabe A2: Effizienz der linearen Suche
- Wie effizient ist dieser Algorithmus?
- Warum heisst dieser Algorithmus lineare Suche?
Aufgabe A3: Lineare Suche in Python
Betrachte folgenden Datensatz mit Namen und Telefonnummern. Zum Beispiel ist die Telefonnummer von Daniel 0791111111.
names = ['Amos', 'Daniel', 'Ezechiel', 'Habakuk', 'Haggai', 'Hosea', 'Jeremia', 'Jesaja', 'Joel', 'Jona', 'Maleachi', 'Micha', 'Nahum', 'Obadja', 'Sacharja', 'Zefanja'] numbers = ['0790000000', '0791111111', '0792222222', '0793333333', '0794444444', '0795555555', '0796666666', '0797777777', '0798888888', '0799999999', '0791234567', '0793213213', '0794565656', '0797898989', '0799876543', '0798767676']
- Implementiere die lineare Suche in einer Funktion
linear_search(li,el)
. Darin soll in der Liste li nach dem Element el gesucht werden. Falls es gefunden wird, soll die Position (Index) des Elements in der Liste zurück gegeben werden (nicht geprintet!). Test: Der Funktionsaufrufprint(linear_search(names,'Haggai'))
soll $4$ ausgeben.
- Nutze nun diese Funktion, um die Telefonnummer von 'Micha' zu ermitteln.
Achtung: Generell bei solchen Aufgaben gilt: Verwende keine vordefinierten Suchfunktionen. Es geht genau darum, dass du diese selbst programmierst.
Tipps (versuche zuerst ohne lösen)|
1. Gehe IN der Funktion die Liste Element für Element durch. 1. Vergleiche jedes dieser Elemente mit demjenigen, nach dem gesucht wird. 1. Sobald es gefunden wurde: Gebe die Position vom Element in der Liste zurück. 1. Greife nun (ausserhalb der Funktion) auf das Element an dieser Position in der *anderen* Liste zu.
=== Aufgabe A4: Postleitzahl === Der {{ :gf_informatik:plz_data.py.zip
Datensatz laden| Der Datensatz ist in einem ZIP-File. Lade dieses herunter und entzippe es (typischerweise: rechte Maustaste / Hier entpacken). Erst nachher ist es verwendbar.
Aufgabe A5: Verbesserte lineare Suche (optional)
Stelle dir vor, du suchst in der bereits alphabetisch sortierten Liste ['albert','christine','dominick','emma']
nach dem 'bert'
. Sobald du beim Suchen also bei 'christine'
angelangst, weisst du, dass du abbrechen kannst und der gesuchte Wert (also 'bert'
) nicht in der Liste enthalten ist. Achtung: Das geht nur, wenn man sicher weiss, dass die Liste sortiert ist.
Erweitere deine lineare Suche um diese Funktionalität. Füge deiner Funktion dazu ein weiteres Argument mit einem Defaultwert is_sorted = False
hinzu. Wenn die Liste nicht sortiert ist, soll die lineare Suche wie bisher ablaufen. Wenn sie sortiert ist, soll sie vorzeitig abbrechen.
Aufgabe A6: Besserer Algorithmus? (optional)
Hast du eine Idee für einen besseren, sprich effizienteren Suchalgorithmus einer bereits sortierten Liste? Bespreche mit der Lehrperson und implementiere danach in Python.
1.2 Binäre Suche
Ist ein Datensatz nicht sortiert, macht eine lineare Suche Sinn. Ist dieser aber bereits sortiert, so gibt es viel effizientere Suchalgorithmen, wie die hier beschriebene binäre Suche:
Wir teilen den Suchbereich ungefähr in der Mitte. Ist der Begriff zufälligerweise gleich dort, haben wir den Eintrag gefunden; ist der Begriff kleiner als das gefundene Element, suchen wir in der ersten Hälfte, sonst in der zweiten Hälfte weiter, bis genau ein einziges Element übrig bleibt. An dieser Stelle befindet sich der gesuchte Begriff, wenn er überhaupt vorhanden ist. Falls nicht, würde er an dieser Stelle eingefügt werden.
Bei der Suche im Wörterbuch suchen wir zu Beginn nur die richtige Seite - wir teilen den Suchbereich indem wir die Mitte der verbleibenden Seiten wählen und den ersten Eintrag der Seite anschauen. Sind wir auf der richtigen Seite, wiederholen wir dasselbe mit den einzelnen Einträgen.
Aufgabe B1: Binäre Suche im Wörterbuch
Setzte euch in kleinen ($2-4$) Gruppen zusammen und arbeitet mit einem Wörterbuch:
- Jemand in der Gruppe sucht ein zufälliges Wort aus.
- Eine andere Person sucht dieses Wort mithilfe der binären Suche.
- Zählt dabei, wie oft ihr blättern (resp. die Seiten aufteilen) müsst.
- Wie oft müsstet ihr Blättern mit der linearen Suche?
- Für ein beliebiges Wort: Was ist eine durchschnittliche Anzahl Teilungen, um auf die richtige Seite zu gelangen?
- Wer in der Klasse musste am häufigsten Blättern? Ist dies überhaupt möglich? Wir werden sehen …
Aufgabe B2: Binäre Suche berechnen
- Eine Broschüre hat 8 Seiten und du möchtest mithilfe der binären Suche eine bestimmte Seite finden. Wie oft musst du im worst case blättern? Tipp: Zeichne auf einem Blatt vor dir 8 Striche, die die Seiten repräsentieren.
- Wie sieht es mit einer Broschüre mit 16 oder 32 Seiten aus? Wie mit einem Buch mit 256 Seiten?
- Erkennst du ein Schema in den letzten zwei Aufgaben?
- Du liest gern dicke Fantasy-Bücher. Mit wie oft blättern kannst du eine beliebige Seite finden?
- Stelle dir vor, es gäbe ein Telefonbuch für alle $8$ Milliarden Menschen, wobei jede Person eine einzelne Seite hätte. Wie oft musst du hier maximal blättern, um eine beliebige Person zu finden?
Aufgabe B3: Binäre Suche in Python
Ziel: Die binäre Suche in einer Funktion binary_search(li,el)
implementieren. Die Funktion soll den Index von Element el
in der Liste li
zurückgeben. Kommt das Element nicht vor, soll None
zurückgegeben werden.
Vorgehen:
- Arbeitet in 2er Gruppen
- Überlegt euch, wie ihr den Algorithmus implementieren könnt.
- Legt die ausgeteilten Papierchen mit Nummern sortiert auf den Tisch. Arbeitet mit diesen, um eure Ideen auszuprobieren.
- Implementiert die Suche. Starte mit dem Code-Template unten.
Schreibt man eine Funktion, so ist es wichtig, diese gut zu testen. Am besten tests du jeweils gleichzeitig mehrere Fälle, wie im folgenden Template gezeigt:
def binary_search(li,el): # write your code here pass # TEST YOUR FUNCTION print(binary_search(['A','C','D','G','J','N','Q','X'],'B')) # output must be: None print(binary_search(['A','C','D','G','J','N','Q','X'],'A')) # output must be: 0 print(binary_search(['A','C','D','G','J','N','Q','X'],'C')) # output must be: 1 print(binary_search(['A','C','D','G','J','N','Q','X'],'D')) # output must be: 2 print(binary_search(['A','C','D','G','J','N','Q','X'],'G')) # output must be: 3 print(binary_search(['A','C','D','G','J','N','Q','X'],'J')) # output must be: 4 print(binary_search(['A','C','D','G','J','N','Q','X'],'N')) # output must be: 5 print(binary_search(['A','C','D','G','J','N','Q','X'],'Q')) # output must be: 6 print(binary_search(['A','C','D','G','J','N','Q','X'],'X')) # output must be: 7 print(binary_search(['A','C','D','G','J','N','Q','X'],'Z')) # output must be: None
Aufgabe B4: Postleitzahlen
- In wie vielen Schritten findest du jede Postleitzahl im worst case?
- Wende deine binäre Suche auf den Datensatz mit den Postleitzahlen an. Wie heissen die Ortschaften mit der vorgegebenen Postleitzahlen und in welchen Kantonen liegen sie?
- 1234
- 9100
Aufgabe B5: Nach Orten suchen
Was passiert, wenn du statt nach Postleitzahlen nach Orten suchst? Funktioniert die Binärsuche? Weshalb nicht? Was müssten wir ändern, damit sie funktioniert?
Aufgabe B6: Rekursion (optionale Zusatzaufgabe)
Bei der binären Suche gehen wir ja wie folgt vor:
- Index in der Mitte des Suchintervalls wählen.
- Element in der Mitte auslesen.
- Ist dieses Element das richtige, so sind wir fertig.
- Andernfalls wiederholen wir die Suche mit einem kleineren Teilintervall: left oder right entsprechend anpassen un zurück zu Schritt 1.
Schreibe eine neue Funktion, binary_search_recursive(L,x,left=None,right=None)
. Die Funktion codiert die obigen Schritte - statt einer while
-Schleife soll sich Funktion im Schritt 4 wieder selber aufrufen. Dieses Verfahren heisst Rekursion.
Wahrscheinlich ist dir das left=None,right=None
in der Deklaration der Funktion aufgefallen: Es bedeutet, dass wenn die Funktion ohne die diese letzten beiden Elemente aufgerufen werden, also z.B. binary_search_recursive([0,2,4,6,8],2)
, so werden den Variablen left
und right
jeweils der Wert None
zugewiesen.
Rekursion eignet sich für viele Probleme, die sich mit Divide & Conquer (Teile & Herrsche) lösen lassen: Probleme, die wir für den trivialen Fall mit einem Element lösen können, und die wir effizient von einem grösseren in ein kleineres Problem überführen können.
2. Sortieren
Aufgabe 1: Sortieren
Material: Kärtchen mit Zahlen
- Lege die Kärtchen in zufälliger Reihenfolge vor dich auf den Tisch.
- Sortiere dann die Kärtchen mit einer klaren Systematik
- Regeln:
- Du darfst nur die beiden Zeigefinger verwenden.
- Diese kannst du bei Kärtchen positionieren. Du darfst immer nur Kärtchen anschauen, bei denen ein Zeigefinger ist.
- Es sind zwei Bewegungen von Kärtchen erlaubt.
- Kärtchen, bei denen Zeigefinger ist, vertauschen.
- Kärtchen, bei denen Zeigefinger ist, auf neue Ebene bringen.
- Wie gehst du vor? Notiere stichwortartig.
- Versuche verschiedene Sortier-Strategien zu finden. Notiere jede Strategie stichwortartig.
Aufgabe 2: Selectionsort
Teil I
Arbeite wieder mit den Buchstaben-Kärtchen: Lege diese in zufälliger Reihenfolge auf den Tisch. Wende dann den Selectionsort-Algorithmus an, um diese zu sortieren.
Teil II: Python
Implementieren den Selectionsort-Algorithmus in Python. Schreibe dazu eine Funktion selectionsort(l)
. Diese nimmt eine unsortierte Liste l
entgegen und sortiert diese mit dem Selectionsort-Algorithmus.
Test: Wende deinen Sortieralgorithmus auf einige Listen an:
['K', 'F', 'G', 'I', 'H', 'D', 'B', 'C', 'A', 'E']
['C', 'D', 'K', 'I', 'H', 'A', 'G', 'B', 'E', 'F']
Zusatzaufgabe: Zufallsliste erzeugen
Schreibe einen Code, der dir eine Zufallsliste z.B. von Buchstaben erzeugt: random_list(10)
gibt dir dann eine Liste mit 10 Grossbuchstaben in zufälliger Reihenfolge zurück.
Diese Liste kannst du dann verwenden, um deine Sortieralgorithmen zu testen.
Aufgabe 3: Bubblesort
Teil I
Arbeite wieder mit den Zahlen-Kärtchen: Lege diese in zufälliger Reihenfolge auf den Tisch. Wende dann den Bubblesort-Algorithmus an, um diese zu sortieren.
Teil II: Python
Implementieren den Bubblesort-Algorithmus in Python. Schreibe dazu eine Funktion bubblesort(L)
. Diese nimmt eine unsortierte Liste li
entgegen und sortiert diese mit dem Bubblesort-Algorithmus.
Aufgabe 4: Insertionsort (Zusatzaufgabe)
3. Lernziele
- Erkläre, die Idee hinter der linearen Suche.
- Erkläre, die Idee hinter der binären Suche.
- Warum heissen die lineare und binäre Suche jeweils so?
- Erkläre, welche Bedingungen die Daten erfüllen müssen, damit die lineare resp. binäre Suche funktioniert.
- Berechne die Anzahl Suchschritte im Worst Case für die beiden Suchfunktionen.
- Vergleiche die Performance der beiden Suchfunktionen.
- Implementiere die beiden Suchfunktionen selbst in Python.