Teil VIII : Elementare Sortierverfahren 1. Begriffsbestimmung 2. Einfache iterative Sortierverfahren 3. Aufwand der einfachen Sortierverfahren 4. Sortieren durch Zerlegen („Quicksort“) K. Murmann, H. Neumann, Fakultät für Informatik, Universität Ulm, 2001 1. Begriffsbestimmung • Motivation und Einordnung • „Internes Sortieren“ – Schema und Notation 1 Motivation und Einordnung Sortieren Prozeß des Einordnens einer gegebenen Menge von Objekten nach einem bestimmten Ordnungskriterium Ziel : Vereinfachung des späteren Suchens nach Elementen der (dann geordneten) Menge Bsp. : • Telephonbücher (→ alphabetische Ordnung) • Literaturverzeichnisse (→ alphabetische Ordnung oder nach der Reihenfolge der Zitate) • Stichwortverzeichnis / Index in Büchern (→ alphabetische Ordnung) • Wörterbücher / Lexika • Adreßbücher : Kategorien von Sortierverfahren 1. „Internes Sortieren“ Der Datenbestand ist während des Sortierens im Hauptspeicher → alle Elemente sind zugreifbar Bsp. : Sortieren eines Arrays 2. „Externes Sortieren“ Der überwiegende Teil des Datenbestands ist während des Sortierens auf Hintergrundspeichern (Band, Platte) → von jedem Bereich (Stapel) sind nur die obersten Elemente sichtbar Bsp. : Hinweis : Sortieren von sequentiellen Verzeichnissen (‚Files‘) Das Problem läßt sich auf das „interne Sortieren“ zurückführen ! (N. Wirth. Algorithmen und Datenstrukturen. Teubner, 1983) 2 „Internes Sortieren“ – Schema und Notation Allgemeines Sortier-Programm Datenbestand im Arbeitsspeicher Unsortierter Datenbestand Sortierter Datenbestand Prinzip : Wirtschaftliche Verwendung des vorhandenen Speichers ⇒ Sortiermethoden, die Elemente am Ort sortieren (!) und nicht das Feld der Daten duplizieren ! Methoden (Auswahl) Iterative Verfahren Sortieren durch Auswählen, Einfügen, Austauschen, ... („Selection sort“, „Insertion sort“, „Bubble sort“) Rekursive Verfahren „Quicksort“ – Sortieren durch Zerlegung der Daten in Teile, Sortieren durch Austausch Notation geg. : • n Datensätze der Form Sortierschlüssel („key“) Inhalt / Daten → Der Sortierschlüssel kann aus einem oder mehreren Teilfeldern bestehen • Ordnungsfunktion f Schlüssel → ermittelt auf den Schlüsseln eine Ordnungsrelation Daten Bsp. : S O R T I A mit • • A X N G M E P A L E Start Elementgröße (Box) ~ Schlüssel („key“) Buchstaben / Zeichen ~ Information / Daten A E E G I L M N O P R S T X Ziel 3 2. Einfache iterative Sortierverfahren • „Selection Sort“ – Direktes Auswählen • „Insertion Sort“ – Direktes Einfügen • „Bubble Sort“ – Direktes Austauschen „Selection Sort“ – Direkte Auswahl Methode Sortieren durch direktes Auswählen Start : Feld mit N Elementen, arr[1..N] Auswahl eines Elements mit kleinstem Schlüssel Austausch gegen das erste Element im Feld, arr[1] Wiederholung der Schritte 1 und 2 mit den • restlichen N-1 Elementen • restlichen N-2 Elementen : Hinweis : Die Reihenfolge der Sortierung geschieht von „unten-nach-oben“; alternativ kann auch von „oben-nach-unten“ sortiert werden ! 4 Aktuell kleinstes Element aus unsortierter Menge Schema des Algorithmus 1 2 i-1 i k N Vorher : Bereits endgültig sortiertes Feld unsortiertes Feld ( ∀j=1..i-2 ): arr[j] ≤ arr[j+1] k = min( arr[i..N] ) ≥ arr[i-1] 1 2 i-1 i i+1 k N Nachher : Bereits endgültig sortiertes Feld unsortiertes Feld min‘( arr[(i+1)..N] ) ≥ arr[i] arr[i] = k = min‘( arr[i..N] ) arr[i] = max‘( arr[1..i] ) Jedes Element wird max. 1-mal von höherer (Index-) Position nach vorn (zur aktuellen Position) bewegt – und damit endgültig einsortiert ! Beispiel 44 55 12 42 94 18 06 67 i=1: j = 2 .. 8 44 55 12 42 94 18 06 67 i=2: j = 3 .. 8 06 55 12 42 94 18 44 67 i=3: j = 4 .. 8 06 12 55 42 94 18 44 67 i=4: j = 5 .. 8 06 12 18 42 94 55 44 67 i=5: j = 6 .. 8 06 12 18 42 94 55 44 67 i=6: j = 7 .. 8 06 12 18 42 44 55 94 67 i=7: j = 8 .. 8 06 12 18 42 44 55 94 67 06 12 18 42 44 55 67 94 Ende Start Vertauscht mit sich selbst ! Vertauscht mit sich selbst ! Sortierte Liste 5 Algorithmus ‚Selection sort‘ CONST N = ...; TYPE ElemType = SEQUENCE = INTEGER; (* Aufzaehlungstyp ! *) ARRAY [1..N] OF ElemType; PROCEDURE SelectMinPosition( VAR arr : SEQUENCE; sortPos : INTEGER): INTEGER; VAR j, min : INTEGER; BEGIN min := sortPos; FOR j := sortPos+1 TO N DO IF (arr[j] < arr[min]) THEN min := j END END; RETURN min END SelectMinPosition; Merkt sich die Position min(arr[ sortPos+1 .. N] ) des unsortierten Teils von arr Algorithmus ‚Selection sort‘ (cont‘d) PROCEDURE ExchangeElements( VAR arr : SEQUENCE; pos, minPos : INTEGER); VAR buffer : ElemType; BEGIN buffer := arr[minPos]; arr[minPos] := arr[pos]; arr[pos] := buffer END ExchangeElements; buffer min PROCEDURE SelectionSort( VAR arr : SEQUENCE); VAR i, pos : INTEGER; 1 BEGIN FOR i := 1 TO N-1 DO pos := SelectMinPosition(arr, i); ExchangeElements(arr, i, pos) END END SelectionSort; i N Bestimme Austauschelemente 6 Modula-2 Programm ‚SelectionSort‘ „Insertion Sort“ – Direktes Einfügen Methode Sortieren durch direktes Einfügen Effizientes Verfahren zur Sortierung einer kleinen Anzahl von Elementen Analog zum Einfügen von Karten in einem Kartenspiel (Skat, Bridge, Rommé, etc.) Strategie Die Elemente (Karten) werden begrifflich in eine • Ziel-Sequenz arr[1] ... arr[i-1] • Quellen-Sequenz arr[i] ... arr[N] aufgeteilt (Hinweis : Anzahl der Elemente = N; i ist variabel = 0 .. N (Beginn: i = 0, am Ende: i = N)) ≡ ≡ A, mit |A| = i-1 B, mit |B| = N-(i-1) (T.H. Cormen, C.E. Leiserson, R.L. Rivest. Introduction to algorithms. MIT Press, 1990) 7 Aktuell betrachtetes Element aus unsortierter Menge Schema des Algorithmus 1 2 i-1 i N Vorher : Bereits sortiertes Feld, jedoch noch nicht endgültig ! 1 2 k unsortiertes Feld | B | = N – (i – 1) i-1 i i+1 N Nachher : Bereits sortiertes Feld unsortiertes Feld |A|=i |B|=N–i Jedes Element wird max. 1-mal von höherer (Index-) Position nach vorn (zur aktuellen Position) bewegt – und damit endgültig einsortiert ! Beispiel 44 55 12 42 94 18 06 67 j=2: i = 1 .. 1 44 55 12 42 94 18 06 67 i=3: j = 2 .. 1 44 55 12 42 94 18 06 67 i=4: j = 3 .. 1 12 44 55 42 94 18 06 67 i=5: j = 4 .. 1 12 42 44 55 94 18 06 67 i=6: j = 5 .. 1 12 42 44 55 94 18 06 67 i=7: j = 6 .. 1 12 18 42 44 55 94 06 67 i=8: j = 7 .. 1 06 12 18 42 44 55 94 67 06 12 18 42 44 55 67 94 Ende Start Sortierte Liste 8 Algorithmus ‚Insertion sort‘ CONST N = ...; TYPE ElemType = SEQUENCE = INTEGER; (* Aufzaehlungstyp ! *) ARRAY [1..N] OF ElemType; PROCEDURE VAR arr pos key InsertIntoAlreadySortedSequence( : SEQUENCE; : INTEGER; : ElemType); VAR halt : i : BOOLEAN; INTEGER; BEGIN i := pos – 1; 1 halt := FALSE; WHILE (NOT halt) AND (arr[i] > key) DO arr[i+1] := arr[i]; i := i – 1; halt := (i = 0) END; arr[i+1] := key END InsertIntoAlreadySortedSequence; Schlüssel („key“) k N Teilsortierter (unterer) Teil wird von oben nach unten durchlaufen Einzusortierender Schlüssel in freie Position Algorithmus ‚Insertion sort‘ (cont‘d) PROCEDURE InsertionSort( VAR arr : SEQUENCE); VAR j : key : INTEGER; ElemType; N : globale Variable Alternative : Sequenz ~ ADT ⇒ Fkt. length(arr) BEGIN FOR j := 2 TO N DO key := arr[j]; InsertIntoAlreadySortedSequence(arr, j, key) END END InsertSort; 9 „Bubble Sort“ – Direktes Austauschen Methode Sortieren durch direktes Austauschen Sortier-Prinzip des fortgesetzten Vergleichens und ggf. Austauschens von Paaren nebeneinanderliegender Elemente ⇒ Die Elemente können als „Blasen“ („bubbles“) aufgefaßt werden, die bei jedem Durchlauf durch das Feld entsprechend ihres Gewichts (~ Schlüssel) auf eine entsprechende Höhe aufsteigen ! In einem Durchlauf können mehrere Blasen (nacheinander) aufsteigen – bis diese an einem größeren (~ schwereren) Element stehenbleiben z z c u b c a b u a Schema des Algorithmus 1 2 i-1 i Vorher : N x y 1 2 i-1 i Nachher : N x y if x ≤ y y x if x > y Für jeweilige Gesamtdurchläufe durch das Feld : arr[ N ] arr[ N-1] : arr[ 1 ] := max( arr[ 1..N ] ) := max( arr[ 1..(N-1) ] ) := arr[ 1 ] 10 Beispiel 44 55 12 42 94 18 06 67 i=8: j = 2 .. 8 44 12 42 55 18 06 67 94 i=7: j = 2 .. 7 12 42 44 18 06 55 67 94 i=6: j = 2 .. 6 12 42 18 06 44 55 67 94 i=5: j = 2 .. 5 12 18 06 42 44 55 67 94 i=4: j = 2 .. 4 12 06 18 42 44 55 67 94 i=3: j = 2 .. 3 06 12 18 42 44 55 67 94 i=2: j = 2 .. 2 06 12 18 42 44 55 67 94 06 12 18 42 44 55 67 94 Ende Start Sortierte Liste Algorithmus ‚Bubble sort‘ CONST N = ...; TYPE ElemType = SEQUENCE = INTEGER; (* Aufzaehlungstyp ! *) ARRAY [1..N] OF ElemType; PROCEDURE BubbleSort( VAR arr : SEQUENCE); VAR i, j : i 1 2 bereits sortiert i N INTEGER; ? ? ? BEGIN j FOR i := N TO 1 BY –1 DO FOR j := 2 TO i DO Aufsteigen der „Blasen“ (* -- Austausch wie in SelectionSort *) ExchangeElements(arr, j-1, j) END END END BubbleSort; 11 Verbesserungen von „Bubble sort“ 1. Termination Man merkt sich, ob es während des Durchlaufs eine Vertauschung gegeben hat keine Vertauschung ⇒ Termination 2. Symmetrie bisher : Asymmetrie im Aufsteigen der „Blasen“ im schwereren Ende des Feldes leichteren Bsp. : 1 2 3 4 5 6 7 12 18 42 44 55 76 94 06 Lösungsansatz : Änderung der Richtung aufeinanderfolgender Durchläufe ! Aufsteigend – absteigend – aufsteigend – etc. → „Shaker sort“ 94 06 12 18 42 44 55 67 (N. Wirth. Algorithmus und Datenstrukturen. Teubner, 1983, p.87) 1 3. Aufwand der einfachen Sortierverfahren • Allgemeine Analyse der Ausführungszeiten • Aufwand von „Selection sort“ 12 Allgemeine Analyse der Ausführungszeiten Einordnung Ausführungszeit eines Programms = f( Größe + Struktur der Eingabedatenmenge ) Anzahl primitiver Operationen („Schritte“) Anzahl der Elemente (→ möglichst maschinenunabhängig) z.B. - Länge eines Feldes n - Knoten und Kanten (Graphen) Erste Näherung : konst. Zeitbedarf für die Ausführung einer Kommandozeile (z.B. in Pseudocode) Betrachtung von - Vergleichsoperationen (Tests) - Vertauschungen, Zuweisungen : und Der Aufwand für „Selection / Insertion / Bubble sort“ Allgemeine Betrachtung der notwendigen Anzahl von Operationen „Selection sort“ Struktur : • • Feld ‚arr‘ wird von 1 .. N-1 durchlaufen jedesmal wird das Feld zur Bestimmung des min. Elements von der aktuellen Position bis zum Ende durchlaufen ( ) f (n ) ∝ (n − 1)⋅ (c ⋅ n ) = O n 2 Aufwand : „Insertion sort“ Struktur : • • Feld ‚arr‘ wird von 2 .. N durchlaufen jedesmal wird von der aktuellen Position das Feld nach unten durchlaufen, um das aktuelle Element an der richtigen Stelle einzusortieren ( ) f (n ) ∝ (n − 1)⋅ (c ⋅ n ) = O n 2 Aufwand : „Bubble sort“ Struktur : Aufwand : • • Feld ‚arr‘ wird von N .. 1 durchlaufen jedesmal wird von 2 .. aktueller Position das Feld durchlaufen und dabei ggf. zwei nebeneinanderliegende Elemente miteinander vertauscht ( ) f (n ) ∝ n ⋅ (c ⋅ n ) = O n 2 13 Aufwand von „Selection sort“ Struktur – Algorithmus Hinweis : Zusammenfassung aller Unterprogramme, so daß der ganze Sortieralgorithmus als eines Unterprogramm geschrieben wird Kosten Anzahl PROCEDURE SelectionSort(VAR arr : VAR i, min : buffer : SEQUENCE); INTEGER; ElemType; BEGIN FOR i := 1 TO N-1 DO (* -- bestimme Pos. des min. Elements *) min := i; FOR j := i + 1 TO N DO IF ( arr[j] < arr[min] ) THEN min := j END END; 7 (* -- vertausche Element *) 8 buffer := arr[min]; 9 arr[min] := arr[i]; 10 arr[i] := buffer END END SelectionSort; 1 2 3 4 5 6 c1 (*) N N − 1 (*) N −1 N (N + 1) / 2 − 1 (**) N (N N−−11) / 2 (**) ∑i=1 ti 0 c3 c4 c5 c6 0 N −1 c8 c9 c10 N −1 N −1 N −1 Analyse der Schritte (*) bei k Durchläufen stets k+1 Tests ! (**) i j Durchläufe ( 5 ) Tests ( 4 ) (Durchläufe + 1) N −1 1 2 N N −1 N 2 3 N N −2 N −1 3 4 N N −3 N −2 N −1 N N N − (N − 1) = 1 N (N + 1) − N = N (N − 1) 2 2 2 N (N − 1) N (N + 1) + (N − 1) = −1 2 2 14 Ausführungszeit Ausführungszeit des Algorithmus = Summe der Zeiten für jede ausgeführte Anweisung T (n ) = c1n + c3 (n − 1) + c4 n(n + 1) n(n − 1) − 1 + c5 + 2 2 n −1 c6 ∑ ti + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) i =1 die definierte Ausführungszeit des Algorithmus hängt von der Strukturierung der Daten ab ! Wichtig : hier ist die kritische Stelle die Anzahl der Änderungen („updates“) von min in Kommandozeile 6 ! Analyse 1. Bedingung des geringsten Aufwands Feld ist bereits sortiert ! (∀j = (i + 1) ⇒ ti = 0 n ) : arr [i ] ≤ arr [ j ] (in Zeile 5) (in Zeile 6) n(n − 1) n(n + 1) − 1 + c5 + ⇒ T (n ) = c1n + c3 (n − 1) + c4 2 2 c6 0 + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) a = 1 (c4 + c5 )n 2 + c1 + c3 + c4 − c5 + c8 + c9 + c10 n 2 2 2 − (c3 + c4 + c8 + c9 + c10 ) b c → T (n ) ist eine quadratische Funktion von n, an 2 + bn + c ! 15 2. Bedingung des größten Aufwands Feld ist in umgekehrter (absteigender) Ordnung sortiert ! X T S R P O N M L I G E E A 1 A n ⇒ jedes Element arr[ i ] muß mit jedem Element des restlichen Feldes arr[ (i+1)..n ] verglichen und mit einem vertauscht werden ! Wie oft wird ein „update“ von min durchgeführt ? X T S R P O N M L I G E E A 1 A n Start-Zustand P A A E E O N M R L I S T 1 ZwischenErgebnis X G n (i − 1) n 2 (i − 1) n − (i − 1) = n − i + 1 16 n : (n − 2(i − 1)) − mal 2 n ∀j = (i + 1) > : 0 − mal 2 d.h. für ∀j = (i + 1) ≤ ⇒ ti = n − 2( j − 2 ) = n − 2(i − 1) = n − 2i + 2 und für j = 2, 3, .., n/2 ( da j = i+1 ) n/2 n/2 n/ 2 i =1 i =1 i =1 ∑ (n − 2i + 2) = ∑ (n + 2)− 2∑ i so daß n (n + 2 ) 2 n n = n + + 3 2 2 = ⇒ T (n ) 3 n n + 1 2 2 n(n + 1) n(n − 1) = c1n + c3 (n − 1) + c4 − 1 + c5 + 2 2 3 n c6 n + 1 + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) 2 2 a‘ = nn 2 + 1 42 nn = + 1 22 wie vorher ! wie vorher ! c c 3c 1 3 2 c4 + c5 + c6 n + c1 + c3 + 4 − 5 + 6 + c8 + c9 + c10 n 2 2 2 2 2 − (c3 + c4 + c8 + c9 + c10 ) b‘ c‘ → T (n ) ist eine quadratische Funktion von n, a' n 2 + b' n + c' 3 3 wobei a ' = a + c6 , b' = b + c6 , c' = c 2 2 ! 17 3. Mittlerer Aufwand Die n Elemente des Feldes sind zufällig angeordnet ! S O R X T A I N G E M P A L 1 E n ⇒ Wie aufwendig ist die Bestimmung des neuen Elements arr[ i ], d.h. Wie groß ist die Anzahl der „updates“ von min ? intuitive Herangehensweise : 50% in arr[ (j+1) .. n ] kleiner als arr[ i ] im Mittel sind 50% in arr[ (j+1) .. n ] größer als arr[ i ] in der Hälfte aller Fälle ist arr[ j ] < arr[ min ] ⇒ ti = n − i + 1 2 n n − i + 1 n(n + 1) so daß ∑ = 2 4 i =1 ⇒ T (n ) n(n − 1) n(n + 1) − 1 + c5 + = c1n + c3 (n − 1) + c4 2 2 n(n + 1) c6 + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) 4 wie vorher ! wie vorher ! → T (n ) ist eine quadratische Funktion von n, an 2 + bn + c ! 18 Resultat – Aufwand von „Selection sort“ 1. Der Aufwand für „Selection sort“ ist von der Art O(n2) , also quadratisch ! 2. Der Aufwand ist gleich hoch, unabhängig von der Anordnung der Elemente der zu sortierenden Menge ! ∴ Der einzige Programmteil, der von der Struktur der Eingabe abhängt, ist die Anzahl der Neubestimmungen für min (Programm-Zeile 6) ! → Wiederholung und Test werden unabhängig davon immer O(n2)-mal ausgeführt ! Verfeinerung Bisher : alle Operationen (Kommandozeilen) wurden mit individuellen Kosten ci abgerechnet Weiterführung : etwas detailliertere Aussage - Wieviele Vergleichs-Operationen (Tests) ? - Wieviele Datenaustausch-Operationen (Zustandsänderungen) ? ⇒ Festlegung eines abstrakten Maschinenmodells mit bestimmtem Befehls-Umfang, von denen jeder Befehl mit einem bestimmten Einheits-Kostenmaß berechnet wird ! 4. Sortieren durch Zerlegen („Quicksort“) • Struktur des Ansatzes • Quicksort – Wahl des Pivots und Algorithmus • Aufwand für „Quicksort“ 19 Struktur des Ansatzes Defizite der einfachen Verfahren Jedes zu sortierende Element wird stets mit direkt benachbarten Elementen verglichen – und danach mit wiederum benachbarten Elementen. Dadurch benötigen die auf einfachen Vergleichsstrategien beruhenden Verfahren stets einen Aufwand von O(n2) ! Grundidee für eine Alternative Effizientes Sortieren durch Austauschen weit voneinander entfernt gelegener Elemente ! Bsp. : Absteigend geordnete Liste soll aufsteigend geordnet werden ! ⇒ vertausche vertausche vertausche etc. 1↔n, 2 ↔ (n-1) , 3 ↔ (n-2) , Benötigt jedoch a priori Kenntnis über die Struktur (Anordnung der Daten) ! Strukturierung links rechts → „Teile-und-Herrsche“ Paradigma 1. „Teile“ (Zerlegung und Umordnung der Elemente) l A[l Feld : ⇒ q] (∀i ∈ [l r r] l A[l q q+1 q q+1 r A[(q + 1) r ] q ], j ∈ [(q + 1) r ]) : A[i ] ≤ A[ j ] Index q wird im Rahmen der Zerlegung bestimmt ! Hinweis : Die Werte innerhalb der Teilbereiche sind nicht geordnet ! 20 2. „Herrsche“ Die Teilfelder A[l Teilen sortiert ! q ] und A[(q + 1) r ] werden weiter (rekursiv) durch 3. Zusammenfügen (Kombination) → kein weiterer Aufwand nötig Die Teilfelder sind bereits sortiert Das gesamte Feld A[l r ] ist sortiert ! Organisation der Zerlegung : 1. Auswahl eines Elements, „key“, aus den Elementen „key“ (~ Pivot) : Sortierung aller Elemente bzgl. dieses Vergleichs-Elementes ! l 2. Aufbau zweier (Teil-) Regionen : i j r arr Region B: C: (∀x ∈ arr[l (∀y ∈ arr[ j i ]) : x ≤ key r ]) : y ≥ key B C ⇒ arr [i ] ≤ key ≤ arr [ j ] Start : Feld mit Elementen l .. r Auswahl des Pivot-Elements Paare von Elementen arr [i ] und arr [ j ] , die jeweils die Bedingungen arr[i ] ≤ key arr [ j ] ≥ key bzw. verletzen, werden vertauscht („exchange“) ! key l arr[i] arr[j] r Exchange Wiederholung solange, bis i und j sich treffen, d.h. i ≥ j Bedingung definiert den Zerlegungspunkt q 21 Quicksort – Wahl des Pivots und Algorithmus Wahl des Pivots („key“) für ein (Teil-) Feld arr[l..r] Naiver Ansatz : Wähle ein Element zufällig aus ! key l r max(arr [l Problem : ⇒ Alternative : r ]) = arr [r ] und key := arr [r ] Es werden keine Elemente vertauscht und der Zerlegungspunkt wird q = r ~ Endlosschleife ! Wähle das erste Element als Pivot, d.h. key := arr [l ] 1 Beispiel i=0, j=9 key = arr[1] = 44 44 8 44 55 12 42 94 18 06 67 44 55 12 42 94 18 06 67 i i=1, j=7 Start j arr [1] ≥ key 44 i=2, j=6 arr [2] ≥ key 06 55 i=5, j=4 i≥ j 06 18 12 18 12 42 42 94 06 67 arr [7] ≤ key 18 44 67 arr [6] ≤ key 55 44 67 94 55 44 Stop 06 67 Weiter ... 22 Beispiel (cont‘d) 1 i=0, j=4 key = arr[1] = 06 06 4 06 18 12 42 06 18 12 42 i i=1, j=1 j arr [1] ≥ key 06 18 12 42 arr [1] ≤ key 18 12 42 i≥ j Stop 06 Weiter ... Beispiel (cont‘d) 1 i=0, j=3 key = arr[1] = 18 18 3 18 12 42 18 12 42 i j i=1, j=2 arr [1] ≥ key 18 12 42 arr [2] ≤ key i=2, j=2 arr [1] ≥ key 12 18 42 arr [2] ≤ key i≥ j Stop usw. ... 06 18 42 23 Algorithmus ‚Quicksort‘ CONST N = ...; TYPE ElemType = SEQUENCE = INTEGER; (* Aufzaehlungstyp ! *) ARRAY [1..N] OF ElemType; (* -- initialer Aufruf: QuickSort(arr, 1, N) *) PROCEDURE QuickSort( VAR arr : SEQUENCE; left, right : INTEGER); VAR q : N = Länge von ‚arr‘ INTEGER; BEGIN IF (left < right) THEN q := Partition(arr, left, right); (* -- zentraler Teil ... *) QuickSort(arr, left, q); QuickSort(arr, q+1, right) END END QuickSort; Algorithmus ‚Quicksort‘ (cont‘d) PROCEDURE Partition( VAR arr : SEQUENCE; left, right : INTEGER): INTEGER; VAR i, j : key : INTEGER; ElemType; BEGIN key := i := j := arr[left]; left – 1; right + 1; Zur Sortierung in aufsteigender Reihenfolge wird als ‚key‘ das 1. Feldelement gewählt, um mögliche Endlosschleifen zu verhindern ! WHILE (i < j) DO REPEAT Hier: Annehmende Schleifen ... i := i + 1 Abweisende Schleifen mit WHILE ... benötigen hier UNTIL (arr[i] >= key); zusätzlichen Aufwand für INCR(i) und DECR(j) REPEAT j := j - 1 UNTIL (arr[j] <= key); IF (i < j) THEN ExchangeElements(arr, i, j) END; RETURN j (* Trennpunkt fuer Rekursion*) END Partition; 24 Modula-2 Programm ‚QuickSort‘ Aufwand von „Quicksort“ Allgemeine Bewertung → Die Laufzeit hängt davon ab, ob die Zerlegung balanciert oder unbalanciert ist ! Letzteres hängt wiederum von der Wahl des Pivot-Elements „key“ ab ! Laufzeiten 1. Schlechte Zerlegung Zerlegung : 1 1 2 n (n-1) Annahme : unbalancierte Zerlegung tritt bei jedem Schritt auf, z.B. bei bereits vollständig sortierter Sequenz ! Kosten : Zerlegung kostet O(n) und T(1) = O(1) Bezeichnet die Rekursion 25 Rekursion für die Ausführung von ‚Quicksort‘ : T (n ) = T (n − 1) + O (n ) (mit T (1) = O(1) ) n = ∑ O(k ) n k =1 n = O ∑ k k =1 = O n2 n −1 1 ( ) n n n−2 1 n−3 1 n n −1 n−2 1 2 1 3 1 2 ( ) O n2 Resultat : Die Zeitkomplexität O(n2) ist im ungünstigsten Fall – dann, wenn die Eingabesequenz bereits vollständig sortiert ist ! 2. Beste Zerlegung Zerlegung : 1 n n/2 n/2 Rekursion für die Ausführung von ‚Quicksort‘ : n n T (n ) = 2T + O(n ) 2 = O(n ⋅ log 2 n ) log 2 n n n/2 n/4 n n/2 n/4 n /8 n /8 n /8 n /8 n/4 n/4 n n /8 n /8 n /8 n /8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 n O (n ⋅ log 2 n ) 26 3. Balancierte Zerlegung (mittlerer Aufwand) Beispiel-Zerlegung (9:1) : 1 n 9/10 n 1/10 n Rekursion für die Ausführung von ‚Quicksort‘ : 9 1 T (n ) = T n + T n + O(n ) 10 10 Analyse / Erläuterungen : Für jede Ebene trägt der Zerlegungsbaum die Kosten n bei, bis die Blätter der Ebene log10n = O(log2n) erreicht sind Die weiteren Ebenen haben geringere Kosten n; der Baum terminiert bei einer Tiefe von log10/9n = O(log2n) n log10 n log10 / 9 n n 1 n 10 1 n 100 1 9 n 10 9 n 100 9 n 100 n 81 n 100 81 n 1000 n 729 n 1000 n ≤n 1 ≤n O (n ⋅ log 2 n ) Resumee : Der Aufwand ist T (n ) = O(n ⋅ log 2 n ) (gilt z.B. auch für 99:1) ! Damit ist im Mittel der Aufwand von ähnlicher Größenordnung wie für die beste Zerlegung ! 27 Sortierverfahren sind von zentraler Bedeutung für die Informatik, da hiermit Daten in eine Ordnung gebracht werden, mit der anschließend effektiv gesucht werden kann (→ Suchverfahren); die Elemente der unsortierten Datenmenge werden am Ort sortiert ! Die zu sortierenden Datensätze bestehen aus Sortierschlüsseln („keys“) und den eigentlichen Inhalten (Daten); die Ordnung aufgrund der Sortierung geschieht nach dem (den) verwendeten Sortierschlüssel(n) Einfache iterative Sortierverfahren wurden vorgestellt, die auf jeweils unterschiedlichen Sortierstrategien beruhen: • Sortieren durch direkte Auswahl („Selection sort“), • Sortieren durch direktes Einfügen („Insertion sort“) und • Sortieren durch direktes Austauschen („Bubble sort“) Alle Verfahren haben einen Berechnungsaufwand (Zeitkomplexität) der Art O(n2) – für eine gegebene Anzahl von Datenelementen n – was auf den sukzessive paarweisen Vergleich benachbarter Elemente in der Sequenz zurückzuführen ist; für den Algorithmus „Selection sort“ wurden detailliert die Fälle des geringsten, größten sowie des mittleren Aufwands analysiert – alle resultieren in O(n2) „Quicksort“ arbeitet rekursiv – nach dem „divide-and-conquer“ Prinzip – wobei die Daten jeweils in Teilmengen mit kleinen bzw. großen Schlüsseln zerlegt werden; die Analyse der Zeitkomplexität resultiert für den allgemeinen Fall in einem Aufwand von O(n·log2n) – nur im (seltenen )Falle einer komplett sortierten Liste ist der Aufwand O(n2) 28