Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
| Vorherige Überarbeitung | |||
| — | gf_informatik:suchen_und_sortieren:binaersuche [2026-05-02 12:30] (aktuell) – [Aufgabe B8: Rekursion (optional)] hof | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| + | Zurück zu [[gf_informatik: | ||
| + | |||
| + | Weiter zu [[gf_informatik: | ||
| + | |||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | ==== Binäre Suche ==== | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | === Aufgabe B1: 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? | ||
| + | |||
| + | * Wie oft musst du blättern (bzw. die Seiten aufteilen), bis du das Wort gefunden hast? | ||
| + | * Wie oft müsstest du durchschnittlich mit der linearen Suche blättern? | ||
| + | * Wieviele Positionen (Finger zwischen den Seiten) musst du dir merken? | ||
| + | |||
| + | === Algorithmus === | ||
| + | |||
| + | Ist ein Datensatz nicht sortiert, macht eine lineare Suche Sinn. Ist dieser aber bereits sortiert, so gibt es viel effizientere Suchalgorithmen, | ||
| + | |||
| + | 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, | ||
| + | |||
| + | #### Aufgabe B2: Binäre Suche berechnen | ||
| + | |||
| + | 1. Eine Broschüre hat 7 Seiten und du möchtest mithilfe der binären Suche eine bestimmte Seite finden. Wie viele Seiten musst du maximal aufschlagen? | ||
| + | 1. Wie sieht es mit einer Broschüre mit 15 oder 31 Seiten aus? Wie mit einem Buch mit 255 Seiten? | ||
| + | 1. Erkennst du einen (mathematischen) Zusammenhang zwischen der Anzahl Seiten und der Anzahl Aufschlagen? | ||
| + | 1. Stelle dir vor, es gäbe ein Telefonbuch für alle 8 Milliarden Menschen, wobei jede Person eine einzelne Seite hätte. Ist eine Seite 0.1mm dick, so wäre dieses Telefonbuch ca. 800km dick, was ungefähr der Distanz Romanshorn - London entspricht. Wie viele Seiten musst du maximal aufschlagen, | ||
| + | |||
| + | <nodisp 1> | ||
| + | ++++Lösung| | ||
| + | |||
| + | 1. Für **7** Seiten sind **3** Halbierungen nötig, bis nur noch eine Seite übrig bleibt. Das heisst, im schlechtesten Fall **3 mal aufschlagen**: | ||
| + | 1. Die mittlere (4.) Seite aufschlagen $\rightarrow$ eine Hälfte fällt weg, drei Seiten in der anderen Hälfte bleiben übrig. | ||
| + | 1. Die mittlere der drei Seiten aufschlagen $\rightarrow$ eine Hälfte fällt weg, eine Seite in der anderen Hälfte bleibt übrig. | ||
| + | 1. Zu dieser einen Seite blättern. | ||
| + | 1. Für **15** Seiten sind **4** Halbierungen nötig. Bei **31** Seiten max. **5 mal blättern**. Und bei **255** Seiten max. **8 mal**. | ||
| + | 1. In einem Buch mit $2^n - 1$ Seiten musst du maximal $n$ mal blättern. Du hast Exponentialrechnung eventuell noch gar nicht in der Mathematik behandelt - aber du kannst dir vorstellen, dass die Seitenzahl sehr viel stärker wächst als die Anzahl Halbierungen. | ||
| + | 1. $2^{33}-1 = 8589934591$, | ||
| + | |||
| + | ++++ | ||
| + | </ | ||
| + | |||
| + | Binäre Suche ist also ein äusserst leistungsfähiger Algorithmus, | ||
| + | |||
| + | 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, | ||
| + | #### Aufgabe B3: Binäre Suche in Python | ||
| + | |||
| + | 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(l) - 1`). | ||
| + | |||
| + | ++++Mehr Hinweise| | ||
| + | In der `while`-Schleife manipulieren wir nun diese Positionen, indem wir eine Position in der Mitte der beiden auswählen: `mitte = ...`. Das Element in der Mitte (`l[mitte]`) können wir nun mit dem gesuchten Element vergleichen. Was tun wir, wenn das Mitte-Element genau der Suche entspricht? Wenn das gesuchte Element kleiner oder grösser als das in der Mitte ist, müssen `links` oder `rechts` angepasst werden - aber wie genau? | ||
| + | |||
| + | Beachte: | ||
| + | * Zeichne die Suche auf Papier auf, um keine Verwirrung mit den Positionen entstehen zu lassen. | ||
| + | * Um eine Ganzzahl-Division (ohne Rest) durchzuführen, | ||
| + | |||
| + | <code python> | ||
| + | print(11 // 2) | ||
| + | >>> | ||
| + | </ | ||
| + | ++++ | ||
| + | |||
| + | <nodisp 1> | ||
| + | [[gf_informatik: | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | < | ||
| + | def binary_search(l, | ||
| + | """ | ||
| + | wenn das Element nicht existiert.""" | ||
| + | links = 0 | ||
| + | rechts = len(l) - 1 | ||
| + | while links <= rechts: | ||
| + | # TODO: Dein Code hier! | ||
| + | pass | ||
| + | return None | ||
| + | |||
| + | # Test | ||
| + | print(binary_search([' | ||
| + | </ | ||
| + | < | ||
| + | def binary_search(l, | ||
| + | links = 0 | ||
| + | rechts = len(l) - 1 | ||
| + | | ||
| + | while links <= rechts: | ||
| + | mitte = (links + rechts) // 2 | ||
| + | element = l[mitte] | ||
| + | print(f' | ||
| + | if element == v: | ||
| + | return mitte # Gefunden! | ||
| + | if v < element: | ||
| + | # Links weitersuchen | ||
| + | rechts = mitte - 1 | ||
| + | else: | ||
| + | # Rechts weitersuchen | ||
| + | links = mitte + 1 | ||
| + | | ||
| + | return None # Nichts gefunden | ||
| + | |||
| + | print(binary_search([' | ||
| + | </ | ||
| + | < | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | #### Aufgabe B4: Binäre Suche für 079 | ||
| + | |||
| + | < | ||
| + | <div slot=" | ||
| + | Kannst du nun die Funktion für die binäre Suche selbständig, | ||
| + | |||
| + | <ul> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ul> | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | |||
| + | def binary_search(l, | ||
| + | """ | ||
| + | wenn das Element nicht existiert.""" | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | |||
| + | def binary_search(l, | ||
| + | left = 0 | ||
| + | right = len(l) - 1 | ||
| + | | ||
| + | while left <= right: | ||
| + | middle = (left + right) // 2 | ||
| + | element = l[middle] | ||
| + | if element == v: | ||
| + | return middle | ||
| + | if v < element: | ||
| + | # Links weitersuchen | ||
| + | right = middle - 1 | ||
| + | else: | ||
| + | # Rechts weitersuchen | ||
| + | left = middle + 1 | ||
| + | | ||
| + | return None # Nichts gefunden | ||
| + | |||
| + | name = ' | ||
| + | index = binary_search(names, | ||
| + | if index is None: | ||
| + | print(' | ||
| + | else: | ||
| + | telnr = numbers[index] | ||
| + | print(' | ||
| + | </ | ||
| + | < | ||
| + | assert binary_search([' | ||
| + | assert binary_search([' | ||
| + | assert binary_search(names,' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | #### Aufgabe B5: Zeitmessung mit linearer und binärer Suche | ||
| + | |||
| + | < | ||
| + | <div slot=" | ||
| + | Vergleiche die lineare Suche mit der binären Suche. Hierzu kannst du von Aufgabe B4 ausgehen: | ||
| + | <ul> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <ul> | ||
| + | < | ||
| + | < | ||
| + | </ul> | ||
| + | < | ||
| + | < | ||
| + | </ul> | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | |||
| + | def binary_search(l, | ||
| + | """ | ||
| + | wenn das Element nicht existiert.""" | ||
| + | # TODO implementieren | ||
| + | |||
| + | def linear_search(l, | ||
| + | """ | ||
| + | wenn das Element nicht existiert.""" | ||
| + | # TODO implementieren | ||
| + | |||
| + | def stopwatch(algo, | ||
| + | """ | ||
| + | |||
| + | stopwatch(binary_search, | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | import time | ||
| + | |||
| + | def binary_search(l, | ||
| + | left = 0 | ||
| + | right = len(l) - 1 | ||
| + | | ||
| + | while left <= right: | ||
| + | middle = (left + right) // 2 | ||
| + | element = l[middle] | ||
| + | if element == v: | ||
| + | return middle | ||
| + | if v < element: | ||
| + | # Links weitersuchen | ||
| + | right = middle - 1 | ||
| + | else: | ||
| + | # Rechts weitersuchen | ||
| + | left = middle + 1 | ||
| + | | ||
| + | return None # Nichts gefunden | ||
| + | |||
| + | def linear_search(l, | ||
| + | for i in range(len(l)): | ||
| + | if l[i] == v: | ||
| + | return i | ||
| + | return None | ||
| + | |||
| + | def stopwatch(algo, | ||
| + | start = time.time() | ||
| + | index = algo(names, name) | ||
| + | telnr = numbers[index] | ||
| + | elapsed = time.time() - start | ||
| + | print(f' | ||
| + | |||
| + | stopwatch(linear_search, | ||
| + | stopwatch(binary_search, | ||
| + | stopwatch(linear_search, | ||
| + | stopwatch(binary_search, | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | #### Aufgabe B6: Nach Telefonnummern suchen. | ||
| + | Was passiert, wenn du statt nach Telefonnummern statt nach Namen suchst? Funktioniert die Binärsuche? | ||
| + | |||
| + | <nodisp 1> | ||
| + | ++++Lösung: | ||
| + | Die Liste muss **sortiert** sein, damit wir Binärsuche verwenden können. Das Telefonbuch ist aber nach Namen sortiert, nicht nach Telefonnummern. Wir müssten ein Kopie anfertigen und beide Listen nach Telefonnummer sortieren. | ||
| + | ++++ | ||
| + | </ | ||
| + | |||
| + | #### Aufgabe B7: Mehrere Suchresultate (optional). | ||
| + | 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, | ||
| + | <code python> | ||
| + | def binary_search2(li, | ||
| + | # dein Code hier! | ||
| + | |||
| + | my_list = [' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | print(binary_search2(my_list,' | ||
| + | </ | ||
| + | |||
| + | <nodisp 1> | ||
| + | ++++Lösung| | ||
| + | <code python> | ||
| + | def binary_search2(li, | ||
| + | results = [] | ||
| + | left = 0 | ||
| + | right = len(li)-1 | ||
| + | while left <= right: | ||
| + | mid = (left + right) // 2 | ||
| + | if li[mid] == el: | ||
| + | while li[mid] == el: | ||
| + | mid -= 1 | ||
| + | mid += 1 | ||
| + | while mid < len(li) and li[mid] == el: | ||
| + | results.append(mid) | ||
| + | mid += 1 | ||
| + | return results | ||
| + | elif li[mid] < el: | ||
| + | left = mid + 1 | ||
| + | else: | ||
| + | right = mid -1 | ||
| + | return None | ||
| + | </ | ||
| + | ++++ | ||
| + | </ | ||
| + | |||
| + | #### Aufgabe B8: Rekursion (optional) | ||
| + | |||
| + | < | ||
| + | <div slot=" | ||
| + | Bei der binären Suche gehen wir ja wie folgt vor: | ||
| + | <ul> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <ul> | ||
| + | < | ||
| + | < | ||
| + | </ul> | ||
| + | </ul> | ||
| + | |||
| + | < | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | |||
| + | def binaere_suche_rekursiv(l, | ||
| + | """ | ||
| + | None, wenn das Element nicht existiert.""" | ||
| + | </ | ||
| + | < | ||
| + | assert binaere_suche_rekursiv(names, | ||
| + | assert binaere_suche_rekursiv(names, | ||
| + | </ | ||
| + | < | ||
| + | from null79 import names, numbers | ||
| + | |||
| + | def binaere_suche_rekursiv(l, | ||
| + | """ | ||
| + | None, wenn das Element nicht existiert.""" | ||
| + | # Falls links und rechts nicht angegeben werden, sind sie None | ||
| + | # Wir setzen sie so, dass sie die ganze Liste umfassen: | ||
| + | links = links or 0 | ||
| + | rechts = rechts or len(l) - 1 | ||
| + | |||
| + | # Links ist grösser als rechts - nicht gefunden. | ||
| + | if links > rechts: | ||
| + | return None | ||
| + | | ||
| + | # 1: Intervall halbieren | ||
| + | mitte = (links + rechts) // 2 | ||
| + | # 2: Element in der Mitte auslesen | ||
| + | element = l[mitte] | ||
| + | # 3: Element gefunden? | ||
| + | if element == suche: | ||
| + | return mitte | ||
| + | # 4: Rekursion | ||
| + | if suche < element: | ||
| + | rechts = mitte - 1 | ||
| + | else: | ||
| + | links = mitte + 1 | ||
| + | return binaere_suche_rekursiv(l, | ||
| + | |||
| + | from null79 import names, numbers | ||
| + | print(f' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ---- | ||
| + | Weiter zu [[gf_informatik: | ||