Zurück zu Lineare Suche
Weiter zu Sortieren
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.
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 binärer Suche den Begriff zu finden. Die erste Person antwortet bei jedem Versuch, ob das gesuchte Wort gefunden ist oder ob es vor- oder nach dem vorgeschlagenen Begriff kommt.
Binäre Suche ist also ein äusserst leistungsfähiger Algorithmus, um in einer sortierten Liste ein gewünschtes Element zu finden. Der Suchaufwand wächst nur logarithmisch: Verdoppelt sich die Seitenzahl, so wächst der Aufwand nur um eine Halbierung; bei der linearen Suche würde sich der Aufwand auch verdoppeln.
Allerdings funktioniert die Binärsuche nur, wenn die Sortierung unserem Such-Schlüssel entspricht. Den Namen zu einer gewünschten Telefonnummer zu finden, ist beispielsweise im klassischen Telefonbuch nicht möglich: die Nummern sind in keiner bestimmten Reihenfolge, wir müssten das ganze Telefonbuch linear durchsuchen, um die gewünschte Nummer zu finden. tel.search.ch
verwendet dazu einen zweiten, invertierten Index, bei dem die Namen nach Telefonnnummer sortiert abgespeichert werden
.
Komplettiere folgenden Code und teste ihn aus. Wir merken uns zwei Variablen, links
und rechts
, die den Suchbereich abstecken: links
ist die Position des kleinsten Elements, rechts
die Position des grössten Elements, das noch in Frage kommt. Zu Beginn ist links
natürlich null, und rechts
ist der Index des letzten Elements (also len(li) - 1
).
Vollständige Erklärung und Anleitung
def binary_search(li, el): """Gibt den Index des gesuchten Elements in l zurück, None, wenn das Element nicht existiert.""" links = 0 rechts = len(li) - 1 while links <= rechts: # Dein Code hier! pass return None # 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
Kannst du nun die Funktion für die binäre Suche selbständig, also möglichst ohne nachschauen schreiben und diese auch verwenden, um in Listen zu suchen? Versuche es:
aufgabe_b4.py
) im gleichen Ordner wie die Datei plz_data.py
.plz_data.py
, die du schon in Aufgabe A3 verwendet hast (import plz_data
).postleitzahlen = plz_data.postleitzahlen()
und ortschaften = plz_data.ortschaften()
.binary_search(li, el)
: Sie soll die Position des Elements el
in der Liste li
zurückgeben. Wenn nichts gefunden wird, soll die Funktion None
zurückgeben.plz
eine Postleitzahl, die es gibt.binary_search()
nach dem Index von plz
in der Liste postleitzahlen
.plz
ändern musst, damit ein neuer, korrekter Satz ausgegeben wird.Vergleiche die lineare Suche mit der binären Suche. Hierzu kannst du von Aufgabe B4 ausgehen (unter neuem Namen speichern):
binary_search()
.linear_search(li, el)
(am besten, du schreibst du sie selbst hin, anstatt sie zu kopieren).suche_ort_mit_zeitmessung(algo, plz)
. Sie hat zwei Argumente: algo
ist der Name der Suchfunktion (Suchalgorithmus), die verwendet werden soll (also binary_search oder linear_search); plz
ist die Postleitzahl, nach der gesucht werden soll. Die Funktion soll mit dem gewünschten Algorithmus den Ort suchen und die Zeit für die Suche messen (import time
, siehe auch Aufgabe A5). Am Ende soll die Funktion einen Text in folgender Form ausgeben: „Die Ortschaft mit Postleitzahl … heisst …. Die Suche dauerte … Sekunden.“Was passiert, wenn du statt nach Postleitzahlen nach Orten suchst? Funktioniert die Binärsuche? Weshalb nicht? Was müssten wir ändern, damit sie funktioniert?
In Aufgabe A6 wird die Funktion für die lineare Suche abgeändert für den Fall, dass das gesuchte Element mehrmals in der Liste vorkommt. Die neue Funktion gibt nicht nur die erste Position des gesuchten Elements zurück, sondern sie gibt eine Liste mit allen Positionen zurück, in denen das gesuchte Element vorkommt. Erstelle eine Funktion binary_search2(li, el)
welche ebenso funktionert. Du kannst natürlich davon ausgehen, dass die Liste sortiert ist. Teste deine Funktion zum Beispiel wie folgt:
def binary_search2(li, el): # dein Code hier! my_list = ['A','A','C','E','E','E','E','E','G','G','G','G'] print(binary_search2(my_list,'A')) # Erwartete Ausgabe: [0, 1] print(binary_search2(my_list,'B')) # Erwartete Ausgabe: None print(binary_search2(my_list,'C')) # Erwartete Ausgabe: [2] print(binary_search2(my_list,'D')) # Erwartete Ausgabe: None print(binary_search2(my_list,'E')) # Erwartete Ausgabe: [3, 4, 5, 6, 7] print(binary_search2(my_list,'F')) # Erwartete Ausgabe: None print(binary_search2(my_list,'G')) # Erwartete Ausgabe: [8, 9, 10, 11] print(binary_search2(my_list,'H')) # Erwartete Ausgabe: None
Bei der binären Suche gehen wir ja wie folgt vor:
element == suche
sind wir fertig.suche < element
, so ist das neue Suchintervall die linke Hälfte.element < suche
, das neue Suchintervall ist die rechte Hälfte.
Schreibe eine neue Funktion, binaere_suche_rekursiv(l, suche, links, rechts)
. Die Funktion codiert die obigen Schritte - statt einer while
-Schleife soll die Funktion sich im Schritt 4 selber wieder aufrufen. Dieses Verfahren heisst Rekursion. Was sind die Startparameter für links
und rechts
?
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.
Weiter zu Sortieren