Hin und wieder wäre es praktisch, man könnte eine
längere Liste schneller als nur linear (oben beginnen, unten
aufhören) durchsuchen. Eigentlich bieten sich hierfür eine
sortierte Strings-Collection (bis 65k Einträge)
oder sortierte Arrays (bis 16k Einträge) in Verbindung mit
binärer Suche an. Doch soll die Liste zur Laufzeit verändert
werden können, so verhindert dies die Collection schon dadurch,
dass sie keine Einträge einfügen, sondern nur anhängen
lässt. Damit ist nicht mal mit viel gutem Willen und Aufwand
eine sortierte Liste sinnvoll zu unterhalten. Denn aufwendig ist es
auch, dies mit einem Array selber zu implementieren.
Liegen die Werte bereits sortiert vor und werden zur Laufzeit
nicht verändert, so kann der folgende Algorithmus
ohne weiteres an Strings-Collection oder Arrays
angepasst werden!
In binsrch.sdw befinden sich Beispiele und der gesamte Programm-Code.
ListBox als sortierte ListeEine Workaround für dieses Problem, das allerdings «nur»
4096 Einträge ermöglicht (genügt aber vielfach), ist
die Verwendung einer sortierten ListBox. Gehört
bereits ein Dialog mit zum Programm, so empfiehlt es sich, die
ListBox auf einer Seite anzulegen, die sonst nicht
benötigt wird (siehe «Vielseitige
Dialoge mit Step»). Ansonsten legt man einen Dialog an,
lädt ihn zur Laufzeit, aber zeigt ihn nicht an. Auf die darin
enthaltenen Kontrollfelder kann dennoch zugegriffen werden:
...
SortDialog.Load()
' verarbeite die sortierte Liste
SortDialog.SortedList.AddItem("Meier")
...
' suche einen Eintrag
Index% = BinarySearch%(SortDialog.SortedList, "Herger")
' entferne Dialog aus dem Speicher
SortDialog.Unoad()
...
Voraussetzung hierfür ist, dass der SortedList die Eigenschaft «Sorted» gegeben wurde. In diesem Fall übernimmt das SO die Sortierung. Es kommen damit viel effizientere Routinen zum Einsatz, als diese in StarBasic zu implementieren wären.
Die binäre Suche gehört wohl ins Programm jedes Programmierkurses. Es ist einer jener grundlegenden Algorithmen, die auch noch zu verstehen sind...
Da wir uns auf die Suche in sortierten Listen beschränken, ist es nicht nötig, die ganze Liste zu durchwühlen. Nehmen wir im folgenden Beispiel einer aufsteigend sortierten Namensliste den Messner, so sind alle davor aufgeführten Werte kleiner, alle danach grösser als dieser.
Wird also zuerst der gesuchte Wert mit demjenigen in der Mitte einer Liste verglichen, so kann der weiter zu durchsuchende Bereich bereits halbiert werden: ist der gesuchte Wert grösser, so kann die erste Hälfte weggelassen werden, sonst die zweite. Verfährt man mit der übrigbleibenden Hälfte wiederum so, so verbleibt noch ein Viertel. Der in jedem Schritt verglichene Wert ist im folgenden Beispiel hervorgehoben dargestellt:
|
1. Schritt |
2. Schritt |
3. Schritt |
4. Schritt |
|---|---|---|---|
|
Maier |
|
|
|
|
Major |
|
|
|
|
Mayer |
|
|
|
|
Meier |
|
|
|
|
Meissner |
|
|
|
|
Merz |
Merz |
Merz |
|
|
Messner |
Messner |
Messner |
Messner |
|
Metzger |
Metzger |
|
|
|
Meyer |
Meyer |
|
|
|
Mezger |
Mezger |
|
|
|
Anzahl Werte |
Anzahl benötigter Vergleiche |
|---|---|
|
10 |
4 |
|
100 |
7 |
|
1000 |
10 |
|
10000 |
14 |
|
100000 |
17 |
Mathematisch liesse sich diese Funktion mit (Log2 n) + 1 beschreiben, wobei n die Anzahl Werte ist ;-).
Der folgende Algorithmus und ein Beispielprogramm sind in binsrch.sdw gespeichert.
Function BinarySearch%(SortedList as Object, ByVal SearchValue$)
Dim first% ' Zeiger auf ersten zu vergleichenden Eintrag
Dim last% ' Zeiger auf letzten zu vergleichenden Eintrag
Dim current% ' Zeiger auf aktuellen Eintrag
first% = 0
last% = SortedList.ListCount - 1
Do While last% >= first%
' "mittleren" Wert des aktuellen Bereiches bestimmen
current% = Fix((first% + last%) / 2)
' aktuellen mit gesuchtem Wert vergleichen
Select Case StrComp(SearchValue$, SortedList.List(current%))
' aktueller Wert zu gross: untere Grenze neu festlegen
Case -1
last% = current% - 1
' aktueller und gesuchter Wert stimmen überein
Case 0
Exit Do
' aktueller Wert zu klein: obere Grenze neu festlegen
Case 1
first% = current% + 1
End Select
Loop
If last% < first% Then
' nix gefunden
BinarySearch% = -1
Else
' Index des gefundenen Wertes zurückgeben
BinarySearch% = current%
End If
End Function
Wer sich intensiver mit Algorithmen und Datenstrukturen auseinandersetzen will, dem kann ich die Lektüre des Buches «Algorithmen» von Robert Sedgewick wärmstens empfehlen! Das gibt's zwar «nur» für C und Pascal, aber mit etwas gutem Willen lassen sich die Routinen problemlos nach Basic (oder Java oder ...) übernehmen.