Teil VIII : Ele m e ntare Sortierverfahren 1. Begriffsbestim m u n g 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. B e g riffsbestim m u n g • • Motivation und Einordnung „Internes Sortieren“ – Schema und Notation Motivation und Einordnung Sortieren Prozeß des Einordnens einer gegebenen Menge von Objekten nach einem bestimmten Ordnungskriterium Ziel : Bsp. : • Vereinfachung des späteren S u c h e n s nach Elementen der (dann geordneten) Menge 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. : 2. Sortieren eines Arrays „ 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) „Internes Sortieren“ – Schema und Notation Allgemeines Sortier-Programm Unsortierter Datenbestand Prinzip : Datenbestand im Arbeitsspeicher Sortierter Datenbestand 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 → ermittelt auf den Schlüsseln eine Ordnungsrelation Schlüssel Daten Bsp. : S O R I A mit • • A X T 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 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] A u s w a h l eines Elements mit kleinstem Schlüssel A u s t a u s c h 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-n a c h -o b e n “ ; alternativ kann auch von „ o b e n -n a c h -unten“ sortiert werden ! 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 arr[i] = k = min‘( arr[i..N] ) arr[i] = max‘( arr[1..i] ) unsortiertes Feld min‘( arr[(i+1)..N] ) ≥ arr[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 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 M o d u l a -2 P r o g r a m m ‚ 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] • aufgeteilt Quellen-Sequenz arr[i] ... arr[N] ( 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) 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 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; „ 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 : 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 ] N 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 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; N V e r b e s s e r u n g e n v o n „ Bubble sort“ 1. Termination Man merkt sich, ob es während des Durchlaufs eine Vertauschung gegeben hat keine Vertauschung 2. ⇒ Termination Symmetrie schwereren bisher : Asymmetrie im Aufsteigen der „Blasen“ im Ende des Feldes leichteren Bsp. : 12 18 42 44 55 76 94 06 1 2 3 4 5 6 7 Lösungsansatz : Änderung der Richtung aufeinanderfolgender Durchläufe ! Aufsteigend – absteigend – aufsteigend – etc. → „ Shaker sort“ 94 1 06 12 18 42 44 55 67 (N. Wirth. Algorithmus und Datenstrukturen. Teubner, 1983, p.87) 3. A u f w a n d d e r einfachen Sortierverfahren • • Allgemeine Analyse der Ausführungszeiten Aufwand von „Selection sort“ 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. § Erste Näherung : konst. Zeitbedarf für die Ausführung einer Kommandozeile (z.B. in Pseudocode) § Betrachtung von - Vergleichsoperationen (Tests) - Vertauschungen, Zuweisungen : und - Lä nge eines Feldes n - Knoten und Kanten (Graphen) D e r A u f w a n d f ü r „ Selection / Insertion / Bubble sort“ Allgemeine Betrachtung der notwendigen Anzahl von Operationen § „ Selection sort“ Struktur : • • ( ) 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 : § 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 „ 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 A u f w a n d v o n „ 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 1 FOR i := 1 TO N-1 DO 2 (* -- bestimme Pos. des min. Elements *) 3 min := i; 4 FOR j := i + 1 TO N DO 5 IF ( arr[j] < arr[min] ) THEN 6 min := j END END; 7 (* -- vertausche Element *) 8 buffer := arr[min]; 9 arr[min] := arr[i]; 10 arr[i] := buffer END END SelectionSort; c1 0 c3 c4 c5 c6 (*) N N − 1 (*) N −1 N (N + 1) / 2 − 1 (**) N (N N−−11) / 2 (**) ∑i=1 ti 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 2K N N −1 N 2 3K N N −2 N −1 3 4K N N −3 N −2 M M M M N −1 NKN N − ( N − 1) = 1 2 N (N + 1) − N = N (N − 1) 2 2 N (N − 1) N (N + 1) + (N − 1) = −1 2 2 Ausführungszeit Ausführungszeit des Algorithmus = S u m m e d e r 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. B e d i n g u n g d e s geringsten A u f w a n d s Feld ist bereits sortiert ! (∀j = (i + 1)K n ) : arr[i ] ≤ arr[ j ] (in Zeile 5 ) ⇒ ti = 0 (in Zeile 6 ) n(n − 1) n(n + 1) ( ) T n = ( ) c n + c n − 1 + c − 1 + c + ⇒ 5 1 3 4 2 2 c6 0 + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) a → T (n ) 1 c4 c5 2 ( ) c + c n + c + c + − + c + c + c 1 3 = 4 5 8 9 10 n 2 2 2 − (c3 + c4 + c8 + c9 + c10 ) b c ist eine quadratische Funktion von n, an 2 + bn + c ! 2. B e d i n g u n g d e s größten A u f w a n d s 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 G 1 ZwischenErgebnis X n (i − 1) n 2 (i − 1) n − (i − 1) = n − i + 1 d.h. für ∀j = (i + 1) ≤ ∀j = (i + 1) > : (n − 2(i − 1)) − mal n : 0 − mal 2 n 2 ⇒ ti = n − 2( j − 2 ) = n − 2(i − 1) = n − 2i + 2 so daß 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 = n n 3 n + + 2 2 = 3 n n + 1 2 2 n (n + 2 ) 2 nn 2 + 1 42 nn = + 1 22 ⇒ T (n ) = c n + c (n − 1) + c n(n + 1) − 1 + c n(n − 1) + 1 3 4 5 2 2 3 n c6 n + 1 + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) 2 2 a‘ wie vorher ! wie vorher ! 1 3 2 c4 c5 3c6 + + + + + − + + + + c c c n c c c c c 1 3 = 4 5 6 8 9 10 n 2 2 2 2 2 − (c3 + c4 + c8 + c9 + c10 ) b‘ c‘ → T (n ) a ' n 2 + b' n + c ' ist eine quadratische Funktion von n, wobei 3 a ' = a + c6 2 , 3 b ' = b + c6 2 , c' = c ! 3. Mittlerer Aufwand Die n Elemente des Feldes sind zufällig angeordnet ! S A O R X T I N G E M P A 1 L 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 wie vorher ! n(n − 1) n(n + 1) ⇒ T (n ) = c1n + c3 (n − 1) + c4 − 1 + c5 + 2 2 n(n + 1) + c8 (n − 1) + c9 (n − 1) + c10 (n − 1) c6 4 → T (n ) ist eine quadratische Funktion von n, an 2 + bn + c wie vorher ! ! Resultat – A u f w a n d v o n „ Selection sort“ O(n 2 ) 1. Der A u f w a n d für „ Selection sort“ ist von der Art , 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(n 2 )- mal ausgeführt ! Verfeinerung Bisher : alle Operationen (Kommandozeilen) wurden mit individuellen Kosten c i abgerechnet Weiterführung : etwas detailliertere Aussage - Wieviele Vergleichs- Operationen (Tests) ? - Wieviele D a t e n a u s t a u s c h - Operationen (Zustandsänderungen) ? ⇒ Festlegung eines abstrakten Maschinenmodells mit bestimmtem Befehls-Umfang, von denen jeder Befehl mit einem bestimmten Einheits- K o s t e n m a ß berechnet wird ! 4. Sortieren durch Zerlegen („Quicksort“) • • • Struktur des Ansatzes Quicksort – Wahl des Pivots und Algorithmus Aufwand für „Quicksort“ 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 A u f w a n d v o n O ( n 2 ) ! 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 Feld : r A[l K r ] l A[l K q ] ⇒ q q+1 q q+1 A[(q + 1)K r ] (∀i ∈ [l K q ], j ∈ [(q + 1)K r ]) : A[i ] ≤ A[ j ] Index q wird im Rahmen der Zerlegung bestimmt ! Hinweis : Die Werte innerhalb der Teilbereiche sind nicht geordnet ! r 2. „Herrsche“ [ Die Teilfelder A l K q Teilen sortiert ! 3. ] und A[(q + 1)K r ] werden weiter (rekursiv) durch Zusammenfügen (Kombination) → kein weiterer Aufwand nötig Die Teilfelder sind bereits sortiert Das gesamte Feld A[l K r ] ist sortiert ! Organisation der Zerlegung : 1. Auswahl eines Elements, „ key “, aus den Elementen „ key “ 2. (~ Pivot) : Sortierung aller Elemente bzgl. dieses Vergleichs-Elementes ! l i j r Aufbau zweier (Teil-) Regionen : arr Region B : C : (∀x ∈ arr[l Ki ]) : x ≤ key (∀y ∈ arr[ j K 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 ] ≤ key arr [ j ] ≥ key arr [i ] und arr [ j ] , die jeweils die Bedingungen 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 Quicksort – Wahl des Pivots und Algorithm u s W a h l d e s Pivots („k e y “) für ein (Teil-) Feld arr[l..r] Naiver Ansatz : W ä hle ein Element zufällig aus ! key l r max(arr [l K r ]) = arr [r ] Problem : key := arr [r ] ⇒ und Es werden keine Elemente vertauscht und der Zerlegungspunkt wird q = r ~ Endlosschleife ! Alternative : 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 Weiter ... 67 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 arr [1] ≥ key 06 j 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 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; M o d u l a -2 P r o g r a m m ‚ QuickSort‘ A u f w a n d v o n „ 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 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−2 1 n n n −1 n−3 1 n−2 M 1 2 1 Resultat : Die Zeitkomplexität ist im ungünstigsten Fall O(n 2 ) – dann, wenn die Eingabesequenz bereits vollständig sortiert ist ! 3 1 2 ( ) O n2 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 M 1 1 1 1 1 1 1 1 1 1 1 1 1 1 n O (n ⋅ log 2 n ) 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 : q Für jede Ebene trägt der Zerlegungsbaum die Kosten n bei, bis die Blätter der Ebene log10n = O(log2n) erreicht sind q 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 9 n 10 9 n 100 9 n 100 1 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 ! § 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 a m Ort sortiert ! § Die zu sortierenden Datensätze bestehen aus Sortierschlüsseln („k e y s “) 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 B e r e c h n u n g s a u f w a n d (Zeitkomplexität) der Art O(n 2 ) – 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(n 2 ) § „ 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·log 2 n) – nur im (seltenen )Falle einer komplett sortierten Liste ist der Aufwand O(n 2 )