Sortieren und Suchen IFB 2002 Daniel Jonietz
Überblick Teil 1: Elementare Sortieralgorithmen Teil 2: Quicksort Teil 2: Effizienz von Sortierverfahren Teil 3: Suchalgorithmen
Teil 1 Elementare Sortieralgorithmen
Motivation Eine Reihe von Werten liegt unsortiert vor. Ziel: Die Werte sollen (aufsteigend) sortiert werden.
Datenmodell Die Werte liegen in einer Reihung vor: ... ... tIndex 1 2 1 2 user_max prog_max ... ... tWert tZahlen
Insgesamt höchstens 1000 Werte Daten und Typen const prog_max = 1000; type tWert = integer; tIndex = integer; tZahlen = array [0..prog_max] of tWert; var zahlen: tZahlen; user_max: tWert = 14; Insgesamt höchstens 1000 Werte Verwenden davon nur 15
Sortieren durch Auswahl Idee: Suche das kleinste Element und lege es beiseite. Dann wähle aus den übrigen wiederum das kleinste Element und lege es daneben. Sortieren von Münzen
Auswahl-Sortieren V1 5 7 2 8 3 1 4 1 5 7 2 8 3 4 1 2 5 7 8 3 4 1 2 3 5 7 8 4 1 2 3 4 5 7 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8
Auswahl-Sortieren V2 5 7 2 8 3 1 4 1 7 2 8 3 5 4 1 2 7 8 3 5 4 1 2 3 8 7 5 4 1 2 3 4 7 5 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8 „in situ“
Algorithmus Brauchen Möglichkeit zur Bestimmung des Minimums aus einem Bereich der Zahlen
Minimum und Maximum Aufruf findet statt in procedure TForm1.suche_minimum(Sender: TObject); procedure TForm1.suche_maximum(Sender: TObject);
Aufgabe Implementieren Sie die Funktionen function min(anfang, ende: tIndex): tIndex; function max(anfang, ende: tIndex): tIndex; die den Index eines minimalen bzw. maximalen Elementes aus dem Bereich anfang ... ende liefert. Implementieren Sie damit die Prozedur procedure minsort; Wer möchte, auch procedure maxsort; Idee?
Lösungsvorschlag function min(anfang, ende: tIndex): tIndex; var i, min_pos: tIndex; min_wert: tWert; begin min_pos:= anfang; min_wert:= zahlen[min_pos]; for i:= anfang+1 to ende do if (zahlen[i] < min_wert) then min_pos:= i; end; min:= min_pos; Kandidat besserer Kandidat
Lösungsvorschlag function max(anfang, ende: tIndex): tIndex; var i, max_pos: tIndex; max_wert: tWert; begin max_pos:= anfang; max_wert:= zahlen[max_pos]; for i:= anfang+1 to ende do if (zahlen[i] > max_wert) then max_pos:= i; end; max:= max_pos; Kandidat besserer Kandidat
Lösungsvorschlag procedure minsort; var i, merke: tIndex; begin for i:= 0 to user_max-1 do merke:= min(i, user_max); tausche(zahlen[merke], zahlen[i]); end;
Lösungsvorschlag Idee: Suche immer das Maximum und sammle von rechts procedure maxsort; var i, merke: tIndex; begin for i:= user_max downto 1 do merke:= max(0, i); tausche(zahlen[merke], zahlen[i]); end; Idee: Suche immer das Maximum und sammle von rechts
Sortieren durch Einfügen Idee: Nimm das nächste Element und füge es sortiert ein. Sortieren eines Kartenspiels
Sortieren durch Einfügen 5 7 2 8 3 1 4 5 7 2 8 3 1 4 5 7 2 8 3 1 4 2 5 7 8 3 1 4 2 5 7 8 3 1 4 2 3 5 7 8 1 4 1 2 3 5 7 8 4 1 2 3 4 5 7 8
Sortieren durch Einfügen 5 7 2 8 3 1 4 5 7 2 8 3 1 4 5 7 2 8 3 1 4 2 5 7 8 3 1 4 2 5 7 8 3 1 4 2 3 5 7 8 1 4 1 2 3 5 7 8 4 1 2 3 4 5 7 8 „in situ“
Sortieren durch Einfügen Verschiebeoperationen sind auf Reihungen schwierig zu realisieren Günstig auf anderen Datenstrukturen ( Listen)
Bubblesort Idee: Vergleiche paarweise und schiebe große Elemente nach hinten Sortieren wie Blasen in einem Wasserglas aufsteigen: Große Blasen steigen schnell auf
Bubblesort - Beispiel 5 7 2 8 3 1 4 5 2 7 3 1 4 8 2 5 3 1 4 7 8 2 3 1 4 5 7 8 2 1 3 4 5 7 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8
Algorithmus Ende des Sortiervorgangs: Wenn keine Vertauschung mehr stattfand spätestens nach „Anzahl“ Schleifendurchläufen
Aufgaben Implementieren Sie die procedure bubblesort; Der vorgestellte Bubblesort-Algorithmus kann noch verbessert werden: Die innere Schleife muss nicht immer bis zum Ende durchlaufen werden. Durchdenken Sie dies und verbessern Sie Ihre Implementierung.
Lösungsvorschlag procedure bubblesort; var i: tIndex; getauscht: boolean; begin repeat getauscht:= FALSE; for i:= 0 to user_max-1 do if (zahlen[i] > zahlen[i+1]) then tausche(zahlen[i], zahlen[i+1]); getauscht:= TRUE; end; until not getauscht;
Teil 2 Quicksort
Quicksort Prinzip: Teile und Herrsche Wähle ein Pivot-Element, das das Problem in zwei Teilprobleme zerlegt. Ein Bereich enthält dann alle Elemente, die kleiner sind als das Pivot-Element, der andere Bereich alle Elemente, die größer sind. Löse die Teilprobleme auf die gleiche Art und Weise.
Quicksort - Beispiel 5 1 8 4 3 7 2 2 1 3 4 8 7 5 1 2 3 4 5 7 8 1 2 3 4 5 7 8 1 2 3 4 5 7 8
Quicksort - Pivot-Element Die Wahl des Pivot-Elementes beeinflusst wesentlich die Anzahl benötigter Durchgänge schlecht: p=min() und p=max() gut: p=Zahlen[(links + rechts) div 2] Feld mittleren Wertes optimal: ein Element das den zu sortierenden Bereich in zwei gleich große Teile partitioniert
Quicksort - Zerlegen 5 2 7 4 8 1 3 3 5
Quicksort - Zerlegen 5 2 7 4 8 1 3 3 2 1 7 5
Quicksort - Zerlegen 5 2 7 4 8 1 3 3 2 1 4 8 7 5
Zerlege - Algorithmus
Quicksort - Algorithmus
Aufgaben Schreiben Sie die Funktion function pivot (links, rechts: tIndex): tIndex; Implementieren Sie procedure zerlege (var links, rechts: tIndex); und damit dann insgesamt Quicksort procedure quicksort(anfang, ende: tIndex);
Lösungsvorschlag function pivot (links, rechts: tIndex): tIndex; begin pivot:= zahlen[(links + rechts) DIV 2] end;
Lösungsvorschlag procedure zerlege (var links, rechts: tIndex); var p : tWert; begin p := pivot(links, rechts); repeat while zahlen[links] < p do links:= links +1; while zahlen[rechts] > p do rechts:= rechts -1; ...
Lösungsvorschlag ... if (links <= rechts) then begin tausche (zahlen[links], zahlen[rechts]); links := links +1; rechts := rechts -1; end; until (links > rechts);
Lösungsvorschlag procedure quicksort(anfang, ende: tIndex); var links, rechts: tIndex; begin links:= anfang; rechts:= ende; zerlege (links, rechts); if (anfang < rechts) then quicksort(anfang, rechts); if (links < ende) then quicksort(links, ende); end;
Weitere Sortierverfahren Heapsort Shellsort Bucketsort Platzziffersortieren Mergesort
Teil 3 Effizienz von Sortierverfahren
Kriterien Stabilität Geschwindigkeit Bleiben evtl. vorhandene Vorsortierungen erhalten? Geschwindigkeit Anzahl Vergleiche Anzahl Tauschoperationen Einfluss von Parametern auf den Sortiervorgang Quicksort: Bestimmung des Pivot-Elementes
Stabilität Was passiert mit nach anderen Kriterien bereits sortierten Daten? Angenommen, Blau < Rot 1 4 3 4 1 4 2 1 1 2 3 4 4 4 Auswahl (von links) 1 1 2 3 4 4 4 Auswahl (von rechts)
Stabilität Beispiel: Adressdaten (Telefonbuch o.ä.) Daten: Name Ort ... Sind bereits sortiert nach Ort, sollen jetzt nach Name sortiert werden Die Vorsortierung soll erhalten bleiben!
Komplexität Unterscheiden 3 Fälle: Aufwandsbestimmung Best Case (Average Case) Worst Case Aufwandsbestimmung Messung Zählung und arithmetische Rechnung asymptotische Abschätzung
Experimente Verwenden Zähler zur Abschätzung des Aufwandes
Experimente - Daten 2 neue Zähler: var tausch_z: integer = 0; // Anzahl Tauschoperationen vergleich_z: integer = 0; // Anzahl Vergleichsoperationen
Exkurs - Ereignissteuerung Die Initialisierung der Zähler geschieht beim Aufruf der Sortierprozedur aus der Ereignissteuerung heraus: procedure TForm1.austausch_sortieren(Sender: TObject); begin tausch_z:= 0; vergleich_z:= 0; minsort; zeige_zaehler; zeige_zahlen; end; Zähler initialisieren Verfahren starten Aktuelle Werte anzeigen
Aufgaben Erweitern Sie die Algorithmen so, dass die entsprechenden Zähler erhöht werden. tausche min bzw. max minsort bzw. maxsort bubblesort zerlege quicksort Untersuchen Sie experimentell den Aufwand bei der Sortierung einiger Zahlenreihen. Wie verhalten sich die verschiedenen Verfahren bei bereits sortierten und umgekehrt sortierten Daten?
Lösung (exemplarisch) procedure tausche(var x, y: tWert); var hilf: tWert; begin hilf:= x; x:= y; y:= hilf; tausch_z:= tausch_z +1; end;
Groß-O-Notation Wenn für die Laufzeit T(n) eines Algorithmus gilt: T(n) c*n für eine Konstante c>0 und alle Werte n>n0, so sagt man „T(n) ist in O(n)“ (IdR reicht es den „stärksten“ Ausdruck zu wählen.)
Aufwand minsort Aufwand für die Suche des Minimums: (n Elemente) beim 1. Durchlauf: n-1 Vergleiche beim 2. Durchlauf: n-2 Vergleiche beim n. Duchlauf: n-n Vergleiche Gesamt: Tauschoperationen: in jedem Durchlauf genau eine, also gesamt n-1 Stück in O(n) n (n - i) = .5n²-.5n ~ n² in O(n²) i=1
Die ursprüngliche Fassung Aufwand bubblesort Vergleichsoperationen (best case) beim 1. Durchlauf: n-1 Vergleiche Fertig! Tauschoperationen (best case) keine Die ursprüngliche Fassung
Aufwand bubblesort Vergleichsoperationen (worst case) beim 1. Durchlauf: n-1 Vergleiche beim 2. Durchlauf: n-1 Vergleiche ... beim n. Duchlauf: n-1 Vergleiche Gesamt: n*(n-1) = n²-n in O(n²) Tauschoperationen (worst case) beim 1. Durchlauf: n-1 Austausche beim 2. Durchlauf: n-2 Austausche beim n. Durchlauf: n-n Austausche Gesamt: n (n - i) = .5n²-.5n in O(n²) i=1
Aufwand Quicksort Bestimmung des Pivot-Elementes hier so gestaltet, dass Aufwand zur Bestimmung vernachlässigbar aber: Wahl des Elementes hat großen Einfluß auf den weiteren Aufwand! Ungünstigste Wahl führt zu O(n²)! Vereinfachte Analyse unter folgenden Bedingungen: Anzahl Elemente 2er-Potenz: n=2k Zerlegung halbiert immer Zerlegung: fortwährende Halbierung führt zu Zerlegungstiefe log2 (n) führt insgesamt auf: O(n*log2(n))
Messergebnisse Überblick und Ergebnisse der Zählungen Min/Maxsort: O(n²) Bubblesort: O(n²) Quicksort: O(n*log2(n))
Teil 4 Suchalgorithmen
Suchalgorithmen Zwei grundlegende Strategien: Beispiel Telefonbuch lineares (sequenzielles) Suchen binäres Suchen Beispiel Telefonbuch Suche nach Namen: binär (naja, in etwa) Suche nach Nummer: linear Beispiel Reihe von Zahlen Suche nach Minimum und Maximum: linear
Lineares Suchen - Bsp Suche nach Element 11 1 3 5 7 11 13 17 1 3 5 7 möglich 1 3 5 7 11 13 17 unmöglich 1 3 5 7 11 13 17 Testelement Treffer
Lineares Suchen Prüfe der Reihe nach alle Elemente ab, bis das gesuchte Element gefunden ist oder keine Elemente mehr da sind. keine Voraussetzungen an die Daten, dürfen auch unsortiert sein.
Lineares Suchen - Alg.
Binäres Suchen Voraussetzung: sortierte Daten Idee: Beginne mit der Suche in der Mitte der Daten; wenn der Suchschlüssel kleiner ist suche in der Mitte des rechten Bereiches weiter, wenn der Suchschlüssel größer ist in der Mitte des linken Bereiches.
Binäres Suchen - Beispiel Suche nach Element 11 1 3 5 7 11 13 17 1 3 5 7 11 13 17 1 3 5 7 11 13 17 1 3 5 7 11 13 17 möglich unmöglich Testelement Treffer
Binäres Suchen - Alg.
Aufgabe Vereinbarung: Wenn das Gesuchte nicht in den Daten enthalten ist soll -1 zurückgegeben werden! Erweiterung der Datenstruktur: type tIndexFehler = integer; Implementieren Sie function lineare_suche(wonach: tWert): tIndexFehler; function binaere_suche(wonach: tWert): tIndexFehler;
Lösung function lineare_suche(wonach: tWert): tIndexFehler; var i: tIndex; position: tIndexFehler; begin position:= -1; i:= 0; repeat if zahlen[i] = wonach then position:= i; i:= i+1; until (position <> -1) OR (i-1 = user_max); lineare_suche:= position; end;
Lösung function binaere_suche(wonach: tWert): tIndexFehler; var links, rechts, position : tIndex; begin links:= 0; rechts:= user_max; // hier eigentliche Suche ... if (wonach = zahlen[position]) then binaere_suche:= position else binaere_suche:= -1; end;
Lösung // das hier ist die eigentliche Suche: repeat position:= (links + rechts) div 2; if (wonach < zahlen[position]) then rechts:= position -1; else if (wonach > zahlen[position]) then links:= position +1; until (wonach = zahlen[position]) //Treffer! OR (links > rechts); //nicht gefunden
Lineare vs. Binäre Suche Verdoppelt sich die Anzahl Elemente, so führt dies schlimmstenfalls zu einer Verdopplung des Aufwandes bei linearer Suche einem zusätzlichen Vergleich bei binärer Suche Einbau von Zählern verdeutlicht dies: var linear_z: integer = 0; binaer_z: integer = 0;
Übung Erweitern Sie die Suchfunktionen, indem Sie die Zähler für jeden benötigten Suchschritt (jeden benötigten Vergleich) erhöhen function binaere_suche(wonach: tWert): tIndexFehler; function lineare_suche(wonach: tWert): tIndexFehler;
Such-Experimente
Weitere Suchverfahren Fibonacci-Suche Sprungsuche