Teil VIII : Elementare Sortierverfahren 1. Begriffsbestimmung

Werbung
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‘
=
nn 
2  + 1
42 
nn 
=  + 1
22 
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
Herunterladen