Suche in Datenfeldern ◦ Zusammenfassung ◦ ?

Wer im Telefonbuch sucht, kann drei Suchstrategien einsetzen:

- Er weiss, auf welcher Seite sich ein Eintrag befindet und springt direkt auf diese Seite.

- Er kennt zwar die Telefonnummer, weiss aber den Namen des Inhabers nicht. Er muss deshalb alle Einträge hintereinander durchzugehen.

- Er kennt den Namen eines Nummerninhabers. Um die Nummer zu ermitteln, durchsucht er das Telefonbuch nach dem Prinzip des binären Suchens. Weil alle Namen alphabetisch sortiert sind, kann er den gesuchten Inhaber durch alphabetische Vergleiche solange einkreisen, bis er ihn gefunden hat. Wenn er zum Beispiel den Namen "Keller" sucht, dann weiss er, dass er in der ersten Telefonbuchhälfte suchen muss. Diese Hälfte kann er wiederum halbieren und in der richtigen Hälfte weitersuchen etc.

Die folgende Tabelle fasst die drei Suchstrategien zusammen und nennt ihre Vor- und Nachteile. Eine schnelle Suchstrategie stellt grössere Anforderungen an die Organisation der Daten und an das Vorwissen des Benutzers:

Suche | Suchbeispiel | Bedingung | Effizienz
direkt | Seite 143 | Seiten nummeriert | sehr schnell
sequentiell | 061 / 422 13 07 | keine | langsam
binär | Bohnenblust Fritz | Einträge sortiert | schnell

Ein konventionelles Telefonbuch ist ähnlich wie ein Datenfeld organisiert. Eine Seite entspricht einer Zelle, und eine Seitennummer entspricht dem Index. Wir bilden deshalb die drei Suchstrategien auf unser Datenfeld Läufer ab.

### 1 Direkte Suche

Direkt suchen heisst mit einem gegebenen Indexwert ein Feldelement bestimmen. Wenn zum Beispiel nach dem Namen des dritten Läufers gefragt wird, so lautet die Antwort:

>     Name = Läufer(3)

### 2 Sequentielle Suche

Sequentiell suchen heisst hintereinander jedes Element mit einem Suchwert vergleichen, bis die Suche Erfolg hat. Wenn zum Beispiel nach der Nummer der Läuferin Dora gefragt wird, so lautet die Antwort: Vergleiche der Reihe nach alle Elemente, bis die Nummer auftaucht:

>     For Nummer = 1 To 5
>       If Läufer(Nummer) = “Dora” Then
>         MsgBox “Nummer von Dora = ” & Nummer
>         Exit For
>       End If
>     Next Nummer

Die Zählschleife (For ... Next) geht solange von Zelle zu Zelle, bis der Inhalt des laufenden Elements mit “Dora” übereinstimmt. Dann wird in einem Ausgabefeld “Nummer von Dora =” und die gefundene Nummer ausgegeben (& verknüpft die Stringkonstante mit Nummer und konvertiert dabei die Zahl Nummer automatisch in einen String). Weil die Suche nun beendet ist, verlässt die Anweisung Exit For die Zählschleife. Wir erweitern deshalb die Syntax der Zählschleife:

> Dim Index As Integer
> For Index = <Untergrenze> To <Obergrenze>
> <Anweisungen>
> [Exit For]
> <Anweisungen>]
> Next Index

Exit For steht in eckigen Klammern, weil es weggelassen werden kann, falls die Schleife nicht mitten im Schleifenkörper verlassen werden muss.

Schreiben Sie die sequentielle Suche b) mit einer While-Schleife.

Übung Maximum

### 3 Binäre Suche

#### a) Spezifikation

Wer im Telefonbuch binär sucht, ...

- springt in die Mitte

- schaut, ob der Suchwert in der linken oder rechten Hälfte liegt

- setzt die Schritte 1 und 2 in der gefundenen Hälfte fort, bis er nach weiteren Halbierungen die Seite mit dem Suchwert gefunden hat.

Eine solche Suche heisst Binärsuche. Sie halbiert ein sortiertes Datenfeld solange, bis das gesuchte Element gefunden wird. Bevor wir die Binärsuche in VBA ausdrücken, beschreiben wir sie entwurfssprachlich. Wir verwenden dazu das Datenfeld Läufer und starten mit dem Beispielaufruf ...

>     sucheBinär(1,7, "Paul", 4)

Das erste Argument enthält die Untergrenze des zu durchsuchenden Feldbereichs. Beim ersten Aufruf von sucheBinär deckt der Feldbereich noch das ganze Datenfeld Läufer ab Spätere Aufrufe durchsuchen einen immer kleineren Teil. Beim ersten Aufruf ist deshalb die Obergrenze 7. Das dritte Argument entspricht dem Suchwert “Paul” und das vierte Argument nennt die Mitte des Feldbereichs. Vor dem ersten Aufruf ist die Mitte (1 + 7) / 2 = 4. Vor jedem weiteren Aufruf wird die Mitte neu berechnet.

Die folgende Tabelle erweitert unser Läuferbeispiel um die Zellen 6 und 7 und zeigt in den Schritten 1 bis 3 die binäre Suche nach “Paul”:

Nr | 1 | 2 | 3 | 4 | 5 | 6 | 7
Läufer(Nr) | ANNA | BRUNO | DORA | FRANZ | KARIN | NORA | PAUL
Schritt (1) | L |  |  | M |  |  | R
Schritt (2) |  |  |  |  | L | M | R
Schritt (3) |  |  |  |  |  |  | L, M, R

> Links Indexwert der Untergrenze des jeweils betrachteten Feldbereichs
> Mitte Indexwert der Mitte des jeweils betrachteten Feldbereichs
> Rechts Indexwert der Obergrenze des jeweils betrachteten Feldbereichs

Gesucht sei die Startnummer von Paul. In der Tabelle erkennen Sie sofort das Suchergebnis 7. Weil der Computer nicht über die menschliche Fähigkeit des unmittelbaren Erkennens von Mustern verfügt, müssen wir ihm die Binärsuche schrittweise beibringen (Schritt in Klammern):

> Zuerst ermittelt der Algorithmus aus den Positionsindizes Links und Rechts den Index Mitte (1). Weil der Läufer der Position Mitte (Franz) nicht gleich Paul ist, sucht der Algorithmus weiter. Paul ist alphabetisch grösser als Franz. Deshalb wird der rechte Teil des Datenfelds weiter untersucht: Die neue Mitte berechnet sich wieder aus Links und Rechts (2). Die Läuferin an der neuen Position von Mitte heisst Nora und ist ebenfalls kleiner als Paul. Weil jetzt der rechte Teil des Datenfelds nur noch aus einer einzigen Zelle besteht, fallen die Positionen Links, Rechts und Mitte zusammen und der Läufer an der Position Mitte entspricht dem Suchwert Paul (3).

#### b) Entwurf

Der verbalen Beschreibung entspricht der folgende entwurfssprachliche Algorithmus. Wir gehen wiederum von gleichen Aufruf aus:

>     sucheBinär(1,7, "Paul", 4)

      sucheBinär(links,rechts, Name, Mitte)
        Mitte = (links + rechts) \ 2 					‘ganzzahlige Division
        Falls links <= Mitte dann
          Falls Name = Feld(Mitte) dann
            Name gefunden
          sonst
            Falls Name < Feld(Mitte) dann
              sucheBinär(links,Mitte-1, Name, Mitte)
            sonst Name > Feld(Mitte)
              sucheBinär(Mitte+1,rechts, Name, Mitte)
        sonst Name nicht gefunden

Der Rückwärtsstrich-Operator \ hinterlässt den ganzzahligen Anteil der Gleitkommadivision /. Zum Beispiel ergibt 5 \ 2 statt der Gleitkommazahl 2.5 die Ganzahl 2. Der Algorithmus ruft sich je nach Bedingung mit einer der fett gedruckten Anweisungen selbst wieder auf. Er heisst deshalb rekursiv (von lat. recurrere = zurücklaufen). Die folgende Geschichte veranschaulicht den Grundgedanken der Rekursion:

>     Ein Hund kam in die Küche und stahl dem Koch ein Ei,
>     da nahm der Koch den Löffel und schlug den Hund entzwei,
>     da kamen viele Hunde und gruben ihm ein Grab und setzten ihm einen Grabstein, worauf geschrieben stand:
>             Ein Hund kam in die Küche und stahl dem Koch ein Ei,
>             da nahm der Koch den Löffel und schlug den Hund entzwei,
>             da kamen viele Hunde und gruben ihm ein Grab und setzten ihm einen Grabstein, worauf geschrieben stand:
>                     ...

Diese Geschichte ist rekursiv, weil sie sich selbst aufruft, indem sie auf die vorangehende Geschichte zurückspringt. Die Katze beisst sich also in den Schwanz. Im Gegensatz zu dieser Hundegeschichte ist aber sucheBinär endlich rekursiv. Ein endlich rekursives Programm ruft sich nur solange auf, bis eine Endbedingung erfüllt wird. In unserem Fall ist die Endbedingung dann erfüllt, wenn der Name auf der Position Mitte gleich dem gesuchten Namen ist.

#### c) Implementation

Wenn Sie auf die Startschaltfläche von sucheBinär.xls klicken, dann erscheint ein Eingabefeld, das Sie nach einem gross geschriebenen Suchnamen - zum Beispiel NORA - frägt (siehe Bild unten). Die gefundene Zellnummer wird dann in einem Ausgabefeld angezeigt, andernfalls erscheint die Fehlermeldung “Kein Element mit diesem Wert”. Ereignisprozedur der Schaltfläche »Suche binär ist die Subroutine sucheBinär() (siehe Code unten).

Damit das Datenfeld Läufer sowohl in der Subroutine sucheBinär() als auch in der aufgerufenden Funktion gefunden gelesen werden kann, vereinbaren wir es mit Private (statt Dim). So ist es im ganzen Modul, und nicht nur in einer bestimmten Prozedur, bekannt:

>     Private Läufer(1 To 7) As String

Besser wäre allerdings eine Vereinbarung von Läufer in der Subroutine sucheBinär() und eine Übergabe des Datenfelds Läufer als Argument an die Funktion gefunden:

>     If gefunden(Läufer, 1,7, Suchname, Nr) = True Then ...

Datenfelder wie Läufer können aber in einzelnen Programmiersprachen, insbesondere in älteren Versionen von Visual Basic, keine Argumente von Prozeduren sein. Wir belassen es deshalb bei der Private-Vereinbarung.

Die folgenden Abbildungen zeigen die Anwender- und Entwicklersicht der Binärsuche. gefunden(links,rechts, Läufername, Mitte) ist eine boolsche Funktion, welche die entwurfssprachliche Version der Binärsuche implementiert. Sie ergibt True, falls der Suchname im Datenfeld vorkommt, sonst False. Die Nummer der gefundenen Zelle wird als Argument Mitte zurückgegeben.

##### Anwendersicht

>     Anwendersicht der Arbeitsmappe sucheBinär.xls

##### Entwicklersicht

    Sub sucheBinaer()
      Dim Nr As Integer, Suchname As String

      Läufer(1) = “ANNA”
      Läufer(2) = “BRUNO”
      Läufer(3) = “DORA”
      Läufer(4) = “FRANZ”
      Läufer(5) = “KARIN”
      Läufer(6) = “NORA”
      Läufer(7) = “PAUL”

      Suchname = InputBox(“Name in Grossbuchstaben: ”)
      If gefunden(1,7, Suchname, Nr) = True Then
        MsgBox “Gesuchte Nummer = ” & Nr
      ElseIf Suchname <> “” ‘ kein Klick auf “Abbrechen”
        MsgBox “Kein Element mit diesem Wert”
      End If

    End Sub

    Ereignisprozedur der Schaltfläche ‘Suche binär’

    Function gefunden( _
      links As Integer, _
      rechts As Integer, _
      Läufername As String, _
      Mitte As Integer) As Boolean

      Mitte = (links + rechts) \ 2	‘ Mitte gibt die gefundene Läufernummer zurück
      If links <= Mitte Then
        If Läufername = Läufer(Mitte) Then
          gefunden = True
        Else
          If Läufername < Läufer(Mitte) Then
            gefunden = gefunden(links, Mitte-1, Läufername, Mitte)
          Else
            gefunden = gefunden(Mitte+1, rechts, Läufername, Mitte)
          End If
        End If
      Else
        gefunden = False
      End If
    End Function

    Binärsuche als VBA-Funktion

Übung sucheIterativBinär

Rekursion in einem Installationsskript (veranschaulicht die praktische Anwendung der Rekursion)