Überblick 5.1 Sequentielles Suchen
Überblick 5.1 Sequentielles Suchen
Überblick 5.1 Sequentielles Suchen
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>Überblick</strong><br />
5 <strong>Suchen</strong> und Sortieren<br />
<strong>5.1</strong> <strong>Sequentielles</strong> <strong>Suchen</strong><br />
5.2 Binäres <strong>Suchen</strong><br />
5.3 Was ist Sortieren? Wie schwer ist es?<br />
5.4 Einige Typen von Sortieralgorithmen<br />
5.5 Einfache Sortieralgorithmen mit Zeitaufwand !(n 2 )<br />
5.6 Eine untere Schranke "(n · log n)<br />
5.7 Quicksort<br />
5.8 Merge Sort<br />
5.9 Ist es möglich, in linearer Zeit zu sortieren?<br />
<strong>5.1</strong>0 Sortiernetzwerke<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
<strong>5.1</strong> <strong>Sequentielles</strong> <strong>Suchen</strong><br />
• Gegeben: Menge von n Elementen, z.B. ganze Zahlen,<br />
abgespeichert in einem Array<br />
int[] a; // a.length = n<br />
• Problem: Ist ein vorgegebenes Element x im Array a enthalten?<br />
• Einfachste Lösung:<br />
<strong>Sequentielles</strong> (oder lineares) Durchsuchen des Arrays<br />
• Triviales Beispiel für einen inkrementellen Algorithmus, der jeweils<br />
ein Datenobjekt nach dem anderen bearbeitet.<br />
• Bei erfolgreicher Suche wird der Index i, 0 ! i < a.length,<br />
zurückgegeben, an dessen Position x steht.<br />
• i = –1 signalisiert erfolglose Suche.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-1<br />
5-2
Algorithmus für sequentielles <strong>Suchen</strong><br />
• int find(int[] a, int x) {<br />
int i = a.length - 1;<br />
while ((i >= 0) && (x != a[i]))<br />
// Position (1)<br />
i--;<br />
// Position (2)<br />
return i;<br />
}<br />
• && (and mit SCE) wertet den booleschen Ausdruck von links nach<br />
rechts aus und bricht die Auswertung ab, sobald der Wert des<br />
booleschen Ausdrucks feststeht:<br />
Auswertung von (i >= 0) zu false verhindert, daß auf ein nicht<br />
existierendes Arrayelement (z.B. a[–1]) zugegriffen wird.<br />
• Invarianten bringen das für den Nachweis der formalen Korrektheit des<br />
Algorithmus Wesentliche zum Ausdruck, nämlich:<br />
In jeder Iteration der while-Schleife wird das Teilarray, von dem<br />
bekannt ist, daß es x nicht enthält, um ein Element vergrößert.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Invarianten und formale Korrektheit<br />
• Folgende Invariante gilt an Position (1):<br />
(I) (0 ! i < a.length) ! (" k, i ! k: a[k] " x)<br />
Invariante (I) gilt trivialerweise beim ersten Durchlauf der while-<br />
Schleife und bleibt wahr in jedem nachfolgenden Durchlauf.<br />
• Folgende Bedingung gilt an Position (2):<br />
(II) (" k, i < k: a[k] " x) !<br />
((i = –1) # ((0 ! i < a.length) ! (a[i] = x)))<br />
Bedingung (II) besagt, daß der Algorithmus auf zwei Arten terminiert:<br />
– i = –1 signalisiert, daß das ganze Array erfolglos durchsucht wurde.<br />
– x wurde an der Arrayposition i gefunden.<br />
• Formaler Korrektheitsbeweis muß zeigen, daß die Schleife wirklich<br />
terminiert:<br />
– i wird mit einem ganzzahligen Wert " 0 initialisiert.<br />
– i wird in jedem Schleifendurchlauf um 1 erniedrigt, muß also nach<br />
einer endlichen Anzahl von Durchläufen negativ werden.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-3<br />
5-4
Einsatz eines Platzhalters<br />
• Boolescher Ausdruck zur Kontrolle der while-Schleife enthält zwei<br />
Bedingungen: (i >= 0) und (x != a[i])<br />
• Erster Term ist verzichtbar, falls sichergestellt ist, daß x immer gefunden<br />
wird, bevor der Suchindex i das Array a verläßt, d.h. i < 0 wird.<br />
• Idee: Erweitere das Array um eine Zelle und speichere die eigentlichen<br />
n Elemente an den Positionen 1, 2, …, n. Benutze a[0] beim Suchprozess,<br />
indem das zu suchende Element x als Platzhalter (sentinel)<br />
zu Beginn des Suchprozesses in a[0] gespeichert wird $<br />
Suchprozess stoppt spätestens bei a[0] $<br />
i > 0 % erfolgreiche Suche i = 0 % erfolglose Suche<br />
• int find(int[] a, int x) {<br />
int i = a.length - 1;<br />
a[0] = x;<br />
while (x != a[i])<br />
i--;<br />
return i;<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Aufwand für sequentielles <strong>Suchen</strong><br />
• Annahme: n Elemente im Array gespeichert<br />
• Schlimmster Fall tritt ein bei erfolgloser Suche:<br />
Gesamtes Array wird durchlaufen $ Zeitaufwand T(n) # !(n)<br />
• Erfolgreiche Suche:<br />
Annahme: Alle im Array enthaltenen Elemente besitzen gleiche<br />
Wahrscheinlichkeit, daß nach ihnen gesucht wird $<br />
durchschnittliche Anzahl Iterationen der while-Schleife bei<br />
erfolgreicher Suche =<br />
1 !(1 + 2 +…+ n) = n +1<br />
n 2<br />
• <strong>Sequentielles</strong> <strong>Suchen</strong> benötigt !(n) Zeit sowohl im schlimmsten als<br />
auch im durchschnittlichen Fall.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-5<br />
5-6
<strong>Suchen</strong> für beliebige Objekttypen<br />
• Falls Datenobjekte vom Typ Element sind, so sollte die Klasse<br />
Element die Methode public boolean equals(Object obj)<br />
bereitstellen, die true zurückgibt, falls das aufrufende Objekt und<br />
das übergebene Objekt gleich sind im Sinne der Anwendung:<br />
class Element {<br />
...<br />
public boolean equals(Object obj) { ... }<br />
...<br />
}<br />
• Stellt die Klasse Element die Methode equals nicht bereit, so wird<br />
die entsprechende Methode der Klasse Object verwendet, die<br />
true zurückgibt, falls beide Objektreferenzen gleich sind!<br />
• Sequentieller Suchalgorithmus:<br />
int find(Object[] a, Object x) {<br />
int i = a.length - 1;<br />
while ((i >= 0) && (! x.equals(a[i])))<br />
i--;<br />
return i;<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5.2 Binäres <strong>Suchen</strong><br />
• Im Array a abgespeicherte Datenelemente seien geordnet bezüglich<br />
einer dem Wertebereich der Datenelemente zugrunde liegenden<br />
Ordnungsrelation !:<br />
" k, 0 ! k < n–1: a[k] ! a[k + 1]<br />
• Suche kann beschleunigt werden, da ein Vergleich des Suchelementes<br />
x mit einem Arrayelement mehr Information gibt als im<br />
ungeordneten Fall:<br />
x " a[m] schließt nicht nur a[m], sondern auch alle Elemente zur<br />
einen oder anderen Seite von m aus:<br />
– x < a[m] $ " i # m: a[i] " x<br />
– x > a[m] $ " i ! m: a[i] " x<br />
a[m] < x<br />
m<br />
Falls<br />
kann x nicht enthalten sein in<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
a[m] > x<br />
m<br />
5-7<br />
5-8
Algorithmus für binäres <strong>Suchen</strong><br />
int find(int[] a, int x) {<br />
int i = 0;<br />
int j = a.length - 1;<br />
while (i a[m])<br />
i = m + 1<br />
else // x = a[m]<br />
// Position (2)<br />
return m;<br />
}<br />
// Position (3)<br />
return -1;<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Invarianten, formale Korrektheit<br />
• Folgende Invariante gilt an Position (1):<br />
(I) (i ! j) ! (" k, 0 ! k < i: a[k] < x)<br />
! (" k, j < k < a.length: a[k] > x)<br />
• Invariante (I) gilt trivialerweise beim ersten Durchlauf der while-Schleife<br />
und bleibt wahr in jedem nachfolgenden Durchlauf.<br />
• Folgende Bedingung gilt an Position (2):<br />
(II) a[m] = x<br />
• Folgende Bedingung gilt an Position (3):<br />
(III) (i = j + 1) ! (" k, 0 ! k < i: a[k] < x)<br />
! (" k, j < k < a.length: a[k] > x)<br />
• i = j + 1 $ Unsicherheitsintervall ist leer!<br />
• In jedem Schleifendurchlauf wird i erhöht oder j erniedrigt $ nach<br />
endlich vielen Schleifendurchläufen wird i > j $ Algorithmus terminiert.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-9<br />
5-10
Zeitaufwand<br />
• Algorithmus funktioniert für jede Wahl von m mit i ! m ! j, ist aber am<br />
effizientesten für m = (i + j) / 2.<br />
• Zeitaufwand im schlimmsten Fall:<br />
&log 2 n' Schleifendurchläufe, d.h. T(n) # !(log n).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Beispiel Interface für Comparable<br />
Binäres <strong>Suchen</strong><br />
• Falls Datenobjekte vom Typ Element int sind, find(int[] so sollte die a, Klasse int x) {<br />
int i = 0;<br />
Element das<br />
int j = a.length - 1;<br />
interface Comparable while (i a[m])<br />
x.compareTo(o)<br />
i = m + 1<br />
3 gibt 4 für 6x < 7o eine 16 negative 23 25 int, 29 für x = o den Wert else 0 und // x = a[m]<br />
für x > o eine positive int zurück.<br />
return m;<br />
}<br />
class Element implements Comparable{<br />
return -1;<br />
i ... m i m ixi iim m j<br />
j }<br />
public int compareTo(Element o) { ... }<br />
int[] a = {3,4,6,7,16,23,25,29};<br />
...<br />
int xi = find(a, 25);<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-11<br />
5-12
Binäres <strong>Suchen</strong> für beliebige Objekttypen<br />
int find(Comparable[] a, Comparable x) {<br />
int i = 0;<br />
int j = a.length – 1;<br />
while (i 0)<br />
i = m + 1<br />
else // comp = 0<br />
return m;<br />
}<br />
return –1;<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5.3 Was ist Sortieren? Wie schwer ist es?<br />
• Motivation: Binäres <strong>Suchen</strong> in geordneten Mengen ist sehr viel schneller<br />
als sequentielles <strong>Suchen</strong> in ungeordneten Mengen. Sortieren ist neben<br />
<strong>Suchen</strong> eine der am häufigsten ausgeführten Operationen.<br />
• Gegeben sei eine Folge S von n Elementen x 1 , x 2 , … , x n , die einem<br />
Wertebereich X entstammen, auf dem eine totale Ordnung ! definiert ist,<br />
d.h. eine Relation, die die folgenden Axiome erfüllt:<br />
! ist reflexiv: " x ( X: x ! x<br />
! ist antisymmetrisch: " x, y ( X: x ! y ! y ! x $ x = y<br />
! ist transitiv: " x, y, z ( X: x ! y ! y ! z $ x ! z<br />
! ist total: " x, y ( X $ x ! y # y ! x<br />
• Sortieren ist ein Prozess, durch den eine Folge x i1 , x i2 , … , x in erzeugt<br />
wird, so daß (i 1 , i 2 , … , i n ) eine Permutation der ganzen Zahlen von 1 bis<br />
n ist und folgende Eigenschaft gilt:<br />
" k, 1 ! k ! n–1: x ik ! x ik+1<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-13<br />
5-14
Sortieren = Finden einer Permutation<br />
• Beispiel:<br />
Sortierung der Folge<br />
x 1 = 9, x 2 = 4, x 3 = 13, x 4 = 2, x 5 = 21, x 6 = 15, x 7 = 2, x 8 = 5, x 9 = 11<br />
erzeugt die Folge<br />
x 4 = 2, x 7 = 2, x 2 = 4, x 8 = 5, x 1 = 9, x 9 = 11, x 3 = 13, x 6 = 15, x 5 = 21<br />
die durch die Permutation<br />
(4, 7, 2, 8, 1, 9, 3, 6, 5)<br />
der Ausgangsfolge festgelegt wird.<br />
• Abstrakte Formulierung:<br />
Sortieren besteht im Auffinden einer spezifischen Permutation (oder<br />
einer unter einigen wenigen Permutationen, wenn Elemente gleiche<br />
Werte haben dürfen) aus n! möglichen Permutationen der n gegebenen<br />
Elemente.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Randbedingungen<br />
• Menge S der zu sortierenden Elemente gegeben in einer Datenstruktur,<br />
die S implizit ordnet, aber nicht notwendigerweise bzgl. der gewünschten<br />
Ordnung !.<br />
• Typische Sortierprobleme:<br />
S gegeben in<br />
– Array mit direktem Zugriff auf jedes Element<br />
(z.B. a[i] im Array a) oder<br />
– sequentieller Datei (z.B. Magnetband) mit sequentiellem Zugriff (z.B.<br />
durch Zeiger auf gegenwärtige Position).<br />
Generierung des Resultats in der gleichen Datenstruktur.<br />
Zugriffsoperationen der zugrunde liegenden Datenstruktur bestimmen,<br />
welche Sortieralgorithmen möglich sind.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-15<br />
5-16
Eigenschaften von Sortieralgorithmen<br />
• Ein Sortieralgorithmus sortiert in place, falls während seines Ablaufs<br />
zu jedem Zeitpunkt höchstens eine konstante Anzahl zu sortierender<br />
Elemente außerhalb der Datenstruktur gespeichert wird.<br />
• Ein Sortieralgorithmus heißt stabil, falls nach der Sortierung die<br />
ursprüngliche Anordnung gleicher Elemente erhalten bleibt:<br />
8, 6', 2', 1, 6", 2", 9 % 1, 2', 2", 6', 6", 8, 9 ist stabil<br />
8, 6', 2', 1, 6", 2", 9 % 1, 2", 2', 6', 6", 8, 9 ist nicht stabil<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Sortieralgorithmen<br />
• Die meisten Sortieralgorithmen sind Verfeinerungen der folgenden Idee:<br />
) while (* (i, j): i < j ! a[i] > a[j]) a[i] :=: a[j];<br />
:=: bezeichnet den Austauschoperator!<br />
• Zwei Typen von Operationen sind zum Sortieren notwendig:<br />
– Sammeln von Information über die Ordnung der gegebenen Elemente<br />
– Ordnen der Elemente, z.B. durch Vertauschen zweier Elemente<br />
• Ziel bei der Entwicklung eines effizienten Sortieralgorithmus:<br />
Minimierung der Anzahl Operationen beider Typen $<br />
– Vermeide, redundante Informationen zu sammeln!<br />
– Versuche, Elemente so selten wie möglich zu bewegen!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-17<br />
5-18
Nicht-deterministische Algorithmen<br />
• Beispiel:<br />
Nicht-deterministischer Algorithmus ) erlaubt beim Sortieren von<br />
x 1 = 5, x 2 = 2, x 3 = 3, x 4 = 4, x 5 = 1 (+)<br />
eine der folgenden 7 Austauschoperationen:<br />
x 1 :=: x i für 2 ! i ! 5 und x j :=: x 5 für 2 ! j ! 4<br />
Zustand (+) könnte entstanden sein durch exotische Strategie, die von<br />
der Mitte zu den beiden Enden sortiert, und es könnte bekannt sein,<br />
daß eine einzige Austauschoperation x 1 :=: x 5 den Sortierprozeß<br />
abschließen würde. Der nicht-deterministische Algorithmus ) bietet<br />
jedoch keine Möglichkeit, dieses Wissen auszudrücken und zu nutzen.<br />
$ Kein Nicht-Determinismus!<br />
Zu entwickelnde Kontrollstruktur muß sorgfältig die Reihenfolge der<br />
auszuführenden Operationen festlegen, um die bereits gesammelten<br />
Informationen möglichst optimal zu nutzen!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Intrinsische Komplexität<br />
• Grenzen der Optimierung:<br />
Ohne irgendwelche bereits vorhandene Informationen muß jedes Element<br />
mindestens einmal inspiziert und i. a. mindestens einmal bewegt<br />
werden $ mindestens "(n) primitive Operationen sind notwendig.<br />
• Sammeln von Informationen nur durch Beantworten binärer Fragen<br />
(z.B. ja/nein Fragen, Vergleich zweier Elemente mit Antwort ! oder >) $<br />
mindestens n · log 2 n Fragen sind notwendig (siehe Abschnitt 5.6) $<br />
"(n · log n) ist untere Schranke in diesem Berechenbarkeitsmodell.<br />
• Umordnen von Elementen: durchschnittlicher Abstand eines Elementes<br />
von seiner korrekten Position $ n / 3 (siehe Abschnitt 4.7).<br />
Transfer eines Elementes kann z.B. durch 1 Schritt der Länge n / 3<br />
oder n / 3 Schritte der Länge 1 erfolgen.<br />
Umordnung nur durch Austausch benachbarter Elemente $<br />
durchschnittlich !(n 2 ) Austauschoperationen sind nötig $<br />
Kurze Schritte nicht ausreichend für effizienten O(n · log n) Algorithmus!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-19<br />
5-20
Praktische Aspekte<br />
• Objekte statt Elemente<br />
Unsere Annahme: Zu sortierende Elemente entstammen einem total<br />
geordneten Wertebereich.<br />
In der Praxis wird die Ordnung der Elemente häufig<br />
– direkt durch Schlüssel bestimmt, die Bestandteil der zu sortierenden<br />
komplexen Datenobjekte sind, die neben dem Schlüssel noch<br />
weitere Daten umfassen, z.B. Konten mit Schlüssel Kontonummer.<br />
– indirekt aus den Eigenschaften der Objekte abgeleitet, z.B.<br />
Rechtecke, die durch ihre Seitenlängen gegeben sind und nach<br />
ihrem Flächeninhalt geordnet werden.<br />
• Vergleichsoperatoren =,
Insertion Sort und Selection Sort<br />
• Hauptunterschied zwischen Insertion Sort und Selection Sort: In<br />
welcher der beiden Mengen wird die meiste Arbeit während des<br />
Sortierens verrichtet?<br />
• Insertion Sort:<br />
Entferne das erste (oder am leichtesten zugreifbare) Element aus<br />
UNSORTED und durchsuche SORTED, um seine korrekte Position zu<br />
finden.<br />
• Selection Sort:<br />
Durchsuche UNSORTED, um das nächste an SORTED anzuhängende<br />
Element zu finden.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Beispiel für Insertion Sort<br />
Initialisierung: SORTED = ();<br />
UNSORTED = (25, 23, 3, 16, 4, 7, 29, 6);<br />
i<br />
1<br />
2<br />
i = 12<br />
34<br />
56<br />
78<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
SORTED<br />
(25)<br />
(23, 25)<br />
(3, 16, 4, 7, 29, 6)<br />
23 25 3 23 25 16 23 4 23 25 16 37 6 25 16 23 16 7 23 16 25 4 25 23 7 29 25 29 6<br />
(3, 23, 25)<br />
(16, 4, 7, 29, 6)<br />
(3, 16, 23, 25)<br />
sortiert sortiert sortiert sortiert sortiert sortiert sortiert unsortiert unsortiert<br />
(3, 4, 16, 23, 25)<br />
(3, 4, 7, 16, 23, 25)<br />
(3, 4, 7, 16, 23, 25, 29)<br />
(3, 4, 6, 7, 16, 23, 25, 29)<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
UNSORTED<br />
(23, 3, 16, 4, 7, 29, 6)<br />
(4, 7, 29, 6)<br />
(7, 29, 6)<br />
(29, 6)<br />
(6)<br />
()<br />
5-23<br />
5-24
Beispiel für Selection Sort<br />
Initialisierung: SORTED = ();<br />
UNSORTED = (25, 23, 3, 16, 4, 7, 29, 6);<br />
i<br />
1<br />
2<br />
(3, 4)<br />
(25, 23, 16, 7, 29, 6)<br />
i i i = = = 23<br />
74 51 6<br />
3<br />
25 3 23 4 25 36 (3, 4, 6)<br />
16 7 23 16 4 16 23 7 29 25 29 25 6<br />
(25, 23, 16, 7, 29)<br />
4<br />
5<br />
6<br />
7<br />
8<br />
SORTED<br />
(3)<br />
(3, 4, 6, 7)<br />
sortiert sortiert sortiert sortiert sortiert sortiert sortiert unsortiert<br />
(3, 4, 6, 7, 16)<br />
(3, 4, 6, 7, 16, 23)<br />
(3, 4, 6, 7, 16, 23, 25)<br />
(3, 4, 6, 7, 16, 23, 25, 29)<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Insertion Sort und Selection Sort<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
UNSORTED<br />
(25, 23, 16, 4, 7, 29, 6)<br />
(25, 23, 16, 29)<br />
(25, 23, 29)<br />
(25, 29)<br />
(29)<br />
• SORTED und UNSORTED teilen sich ein Array, die Grenze zwischen<br />
beiden wird durch einen Index i festgelegt, der mit steigender Anzahl<br />
sortierter Elemente zunimmt.<br />
• Insertion Sort: Füge im i-ten Schritt das i-te Element in die Folge der<br />
ersten (i–1) Elemente ein.<br />
1 2 ? i–1 i n<br />
… ! x ! … x …<br />
sortiert unsortiert<br />
• Selection Sort: Selektiere im i-ten Schritt das kleinste unter den n – i + 1<br />
noch nicht sortierten Elementen und bewege es an die i-te Position.<br />
1 2 i n<br />
… ! x<br />
x = minimum(a[i], …, a[n])<br />
sortiert unsortiert<br />
• Insertion Sort und Selection Sort erfordern wahlfreien Zugriff auf die<br />
Elemente, daher werden beide Verfahren für das interne Sortieren<br />
im Hauptspeicher verwendet.<br />
()<br />
5-25<br />
5-26
Merge Sort<br />
• Merge Sort (Sortieren durch Mischen) verarbeitet (Teil-) Folgen von<br />
Elementen sequentiell $<br />
Merge Sort ist sehr gut geeignet für externes Sortieren auf<br />
Sekundärspeichermedien, die nur sequentiellen Zugriff effizient<br />
unterstützen (z.B. Festplatten) oder erlauben (z.B. Magnetbänder).<br />
• Merge Sort ist auch gut geeignet für internes Sortieren.<br />
• Idee: Verschmelze zwei sortierte Folgen von Elementen, genannt<br />
Läufe (runs), in eine längere sortierte Folge: Lese von jedem der<br />
beiden Läufe das jeweils nächste Element, vergleiche die beiden<br />
Elemente miteinander und hänge das kleinere der beiden Elemente<br />
an die Ausgabefolge.<br />
…<br />
…<br />
9 7 4<br />
6 3 2<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Externer Merge Sort<br />
A 2 1<br />
B 4 3 Prozessor<br />
8<br />
7<br />
1&3<br />
2&4 Prozessor<br />
6 4<br />
• Prozessor liest zwei Magnetbänder (oder sequentielle Dateien) A und B.<br />
– A enthält Läufe 1 und 2, B Läufe 3 und 4.<br />
– Prozessor verschmilzt Läufe 1 und 3 in einen einzigen Lauf 1&3 auf<br />
Band C und Läufe 2 und 4 in einen einzigen Lauf 2&4 auf Band D.<br />
– In einem zweiten Merge-Schritt werden Läufe 1&3 und 2&4 in einen<br />
einzigen Lauf 1&3&2&4, d.h. in eine sortierte Folge, verschmolzen.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
3<br />
2<br />
1&3&2&4<br />
5-27<br />
5-28
Radix Sort …<br />
• Radix Sort<br />
(Sortieren durch Verteilen, Distribution Sort)<br />
– beruht nicht auf Vergleich und Vertauschung von Elementen,<br />
sondern auf deren Darstellung in einem Stellenwertsystem<br />
(z.B. Dezimalsystem).<br />
– benutzt primitive arithmetische Operationen wie<br />
"extrahiere die k-te Ziffer".<br />
• Beispiel:<br />
3-ziffrige Zahlen im Zahlensystem zur Basis 4<br />
301 023 230 322 100 321 213<br />
– Verteile zu sortierende Zahlen auf 4 Folgen gemäß ihrer rechten<br />
Ziffer und hänge die so entstehenden Folgen aneinander.<br />
– Wiederhole diesen Prozess für die mittlere und dann für die linke<br />
Ziffer.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Radix Sort<br />
zu sortierende Folge: 301 023 230 322 100 321 213<br />
301 023 230 322 100 321 213<br />
verteile<br />
verteile bezüglich<br />
bezüglich der rechten 100 321<br />
213<br />
der rechten Ziffer 100 230 321 301 322 213 023<br />
Ziffer 0<br />
230<br />
301 1 2 3<br />
322 023<br />
0 1 2 3<br />
zu sortierende Folge: 230 100 301 321 322 023 213<br />
230 100 301 321 322 023 213<br />
verteile<br />
bezüglich<br />
verteile der mittleren<br />
bezüglich Ziffer<br />
301<br />
100 213 023<br />
023<br />
322<br />
321 230<br />
der<br />
0 1 2<br />
301<br />
322<br />
mittleren<br />
100<br />
213<br />
321 230<br />
Ziffer<br />
zu sortierende Folge: 100 301 213 321 322 023 230<br />
0 100 301 1 213 321 322 2 023 230 3<br />
3<br />
verteile verteile<br />
bezüglich<br />
bezüglich<br />
der linken<br />
der linkenZiffer<br />
Ziffer 023<br />
023<br />
0 100<br />
100<br />
1<br />
230<br />
213<br />
230<br />
213<br />
2<br />
322<br />
321<br />
301<br />
322<br />
321<br />
301<br />
3<br />
sortierte Folge:<br />
023 100 213 230 301 321 322<br />
0 1 2 3<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-29<br />
5-30
5.5 Einfache Sortieralgorithmen mit Zeitaufwand !(n 2 )<br />
• Array von n Elementen, z.B. ganze Zahlen:<br />
int[] a; // a.length = n<br />
Sortiere die Elemente von a in aufsteigender Reihenfolge.<br />
In place Algorithmen benutzen bei der Sortierung neben dem Array a<br />
höchstens O(1) weiteren Speicherplatz.<br />
• Insertion Sort:<br />
void insertionSort(int[] a) {<br />
for (int i = 1; i < a.length; i++) {<br />
int tmp = a[i];<br />
int j = i - 1;<br />
while ((j >= 0) && (tmp < a[j])) {<br />
}<br />
}<br />
a[j + 1] = a[j];<br />
j = j - 1;<br />
}<br />
a[j + 1] = tmp;<br />
• Insertion Sort ist stabil!<br />
Austausch<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Insertion Sort<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
0 1 j i n–1<br />
… …<br />
sortiert unsortiert<br />
• v best , v average , v worst bezeichne die Anzahl Vergleichsoperationen in der<br />
Bedingung der while-Schleife, a best , a average , a worst die Anzahl Durchläufe<br />
des Rumpfes der while-Schleife, d.h. die Anzahl Austauschoperationen,<br />
im besten, durchschnittlichen und schlimmsten Fall.<br />
v best = n !1<br />
v = inv + (n !1) = average average n2 + 3 "n ! 4<br />
4<br />
v worst = i<br />
n!1<br />
# =<br />
i=1<br />
n2 ! n<br />
2<br />
a best = 0<br />
5-31<br />
a = inv = average average n2 ! n<br />
4<br />
a worst = i<br />
n!1<br />
" =<br />
i=1<br />
n2 ! n<br />
2<br />
• inv average bezeichnet die durchschnittliche Anzahl Inversionen in einer<br />
Permutation (siehe Abschnitt 4.7)<br />
• Insertion Sort ist im besten Fall ein !(n) Algorithmus, im<br />
durchschnittlichen und schlimmsten Fall ein !(n 2 ) Algorithmus.<br />
• Binäres <strong>Suchen</strong> der korrekten Einfügeposition von a[i] im bereits<br />
sortierten Teil würde die Zeitkomplexität nicht verbessern!<br />
5-32
Interface Comparable<br />
• Falls Datenobjekte vom Typ Element sind, so sollte die Klasse<br />
Element das<br />
interface Comparable<br />
aus java.lang implementieren, das die Methode<br />
public int compareTo(T o);<br />
enthält. Der Aufruf<br />
x.compareTo(o)<br />
gibt für x < o eine negative int, x = o den Wert 0 und für x > o eine<br />
positive int zurück.<br />
class Element implements Comparable{<br />
...<br />
public int compareTo(Element o) {<br />
...<br />
}<br />
...<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Insertion Sort für beliebige Objekttypen<br />
public static <br />
void insertionSort(T[] a) {<br />
for (int i = 1; i < a.length; i++) {<br />
T tmp = a[i];<br />
int j = i – 1;<br />
while ((j >= 0) &&<br />
(tmp.compareTo(a[j]) < 0)) {<br />
a[j + 1] = a[j];<br />
j = j – 1;<br />
}<br />
a[j + 1] = tmp;<br />
}<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-33<br />
5-34
Selection Sort …<br />
void selectionSort(int[] a) {<br />
for (int i = 0; i < a.length - 1; i++) {<br />
int minindex = i;<br />
for (int j = i+1; j < a.length; j++)<br />
if (a[j] < a[minindex])<br />
minindex = j;<br />
swap(a, i, minindex);<br />
}<br />
}<br />
void swap(int[] a, int i, int j) {<br />
int tmp = a[i];<br />
minindex<br />
a[i] = a[j];<br />
0 i j<br />
n–1<br />
a[j] = tmp;<br />
}<br />
…<br />
sortiert<br />
… …<br />
unsortiert<br />
• Obige in place Implementierung von Selection Sort ist nicht stabil!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Selection Sort<br />
• Die von Selection Sort zu verrichtende Arbeit ist immer gleich, es gibt<br />
keinen besten, durchschnittlichen oder schlimmsten Fall.<br />
Sei v die Anzahl Vergleiche von Elementen, a die Anzahl<br />
Austauschoperationen.<br />
n!2<br />
"<br />
v = (n !1<br />
i=0<br />
a = n !1<br />
! i) = n2 ! n<br />
2<br />
• Selection Sort benötigt immer einen Zeitaufwand von !(n 2 ).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-35<br />
5-36
5.6 Eine untere Schranke "(n · log n)<br />
• Zweiwertige binäre Fragen: Antwort ja / nein, true / false<br />
• Häufige Art einer zweiwertigen Frage: x ! y ?<br />
• Satz:<br />
Jeder Sortieralgorithmus, der die Information über die Anordnung der<br />
Elemente nur über zweiwertige Fragen und nichts anderem bezieht,<br />
benötigt im Durchschnitt (d.h. im Mittel über alle n! Permutationen) und<br />
somit auch im schlimmsten Fall mindestens n · log 2 (n + 1) – n / ln 2<br />
Vergleiche. Somit ist die Zeitkomplexität eines solchen Algorithmus im<br />
durchschnittlichen und im schlimmsten Fall "(n · log n).<br />
Beweis:<br />
Darstellung eines solchen Sortieralgorithmus durch einen binären<br />
Entscheidungsbaum:<br />
– Interner Knoten repräsentiert binäre Frage.<br />
– Blatt entspricht einem möglichen Resultat des<br />
Entscheidungsprozesses.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Eine untere Schranke "(n · log n) …<br />
• Fortsetzung Beweis:<br />
Entscheidungsbaum muß jede der möglichen n! Permutationen der<br />
Eingabedaten von jeder anderen unterscheiden $<br />
Entscheidungsbaum hat mindestens n! Blätter, mindestens eines für<br />
jede Permutation!<br />
Beispiel:<br />
Sortierung von drei Elementen x, y und z durch Vergleiche zwischen<br />
jeweils zwei Elementen<br />
x ! y ?<br />
yes no<br />
y ! z ? y ! z ?<br />
yes no<br />
yes no<br />
x ! y ! z<br />
x ! z ? x ! z ?<br />
yes no yes no<br />
x ! z < y z < x ! y y < x ! z y ! z < x<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
z < y < x<br />
5-37<br />
5-38
… Eine untere Schranke "(n · log n) …<br />
• Fortsetzung Beweis:<br />
Durchschnittliche Anzahl binärer Fragen,<br />
die ein gegebener Sortieralgorithmus benötigt<br />
= durchschnittliche Tiefe der Blätter des Entscheidungsbaums<br />
n " %<br />
log n! = log i<br />
2 2 !<br />
#<br />
$<br />
&<br />
' = log2 i<br />
Lemma (siehe nächste Folie) $ durchschnittliche Tiefe # log2 n!<br />
n<br />
(<br />
i=1<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
i=1<br />
) (n +1)* log (n +1) + 2 n<br />
ln 2 + log (n +1)<br />
2<br />
= n * log 2 (n +1) + n<br />
ln 2<br />
Im Durchschnitt werden also mindestens n · log 2 (n + 1) – n / ln 2<br />
binäre Fragen benötigt, d.h. die Zeitkomplexität eines solchen<br />
Sortieralgorithmus beträgt "(n · log n) im durchschnittlichen und<br />
daher auch im schlimmsten Fall.<br />
… Eine untere Schranke "(n · log n) …<br />
• Lemma:<br />
In einem binären Baum mit k Blättern beträgt die durchschnittliche<br />
Tiefe der Blätter mindestens log 2 k.<br />
Beweis:<br />
durch Widerspruch!<br />
Annahme: Die Behauptung trifft nicht zu!<br />
Der binäre Baum T sei das Gegenbeispiel mit der kleinsten Anzahl<br />
Knoten.<br />
Fall I:<br />
T =<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
w<br />
T besteht aus einem einzigen Knoten $<br />
die Tiefe dieses einzigen Blattes ist 0, die durchschnittliche Tiefe ist<br />
also mindestens log 2 1 = 0 $<br />
Behauptung trifft auf T zu $<br />
T kann nicht nur aus einem einzigen Knoten bestehen.<br />
5-39<br />
5-40
… Eine untere Schranke "(n · log n) …<br />
• Fortsetzung Beweis: w<br />
Fall II:<br />
Fall III:<br />
T =<br />
T =<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
T'<br />
Wurzel w von T hat nur ein Kind $<br />
T' enthält weniger Knoten, aber alle k Blätter von T, die in T' eine<br />
geringere durchschnittliche Tiefe haben als in T $<br />
T' ist Gegenbeispiel mit geringerer Anzahl Knoten als T $<br />
Widerspruch zur Wahl von T $<br />
Wurzel w von T kann nicht nur ein Kind haben.<br />
T L<br />
w<br />
Wurzel w von T hat zwei Kinder: k L > 0 Blätter im linken Teilbaum T L ,<br />
k R > 0 Blätter im rechten Teilbaum T R mit k = k L + k R<br />
… Eine untere Schranke "(n · log n)<br />
• Fortsetzung Beweis:<br />
T Gegenbeispiel mit kleinster Anzahl Knoten $<br />
k L Blätter von T L<br />
k L<br />
log 2 k L<br />
T R<br />
haben durchschnittliche Tiefe<br />
von mindestens<br />
! log k + ! log k +1 (")<br />
2 L 2 R k + k k + k L R<br />
L R<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
k R<br />
k R Blätter von T R<br />
log 2 k R<br />
$ die durchschnittliche Tiefe der k Blätter von T beträgt mindestens<br />
(+) nimmt minimalen Wert log 2 k für k L = k R = k / 2 an<br />
$ durchschnittliche Tiefe der k Blätter von T beträgt mindestens log 2 k<br />
$ Widerspruch zur Wahl von T als Gegenbeispiel!<br />
Fall I, II, III $ Behauptung des Lemmas!<br />
5-41<br />
5-42
5.7 Quicksort<br />
• C. A. R. Hoare: Quicksort, Computer Journal 5(1), 10 – 15 (1962).<br />
• Quicksort kombiniert divide & conquer mit einer effizienten Strategie,<br />
die Elemente mit nur wenigen Austauschoperationen zu bewegen.<br />
• divide:<br />
– Partitioniere das Array in zwei disjunkte Teile, die "kleinen" Elemente<br />
auf der linken Seite und die "großen" Elemente auf der rechten Seite.<br />
conquer:<br />
– Sortiere jedes der zwei Teilarrays rekursiv.<br />
combine:<br />
– Keine Arbeit notwendig, um die beiden Teillösungen zu einer Lösung<br />
des Gesamtproblems zu kombinieren.<br />
kleine Elemente große Elemente<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Quicksort …<br />
… …<br />
• Effizienz von Quicksort hängt entscheidend davon ab, daß die in<br />
der divide-Phase entstehenden Teilarrays sich in der Größe nicht<br />
allzu stark unterscheiden!<br />
• Wähle einen Schwellenwert m $<br />
Definiere klein als ! m, groß als # m<br />
kleine Elemente<br />
! m<br />
… …<br />
große Elemente<br />
# m<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-43<br />
5-44
… Quicksort …<br />
• Partitioniere ein zu sortierendes Teilarray a[L .. R] durch einen<br />
links-nach-rechts Durchlauf L-R (durch Inkrementierung eines Index i )<br />
und einen gleichzeitigen rechts-nach-links Durchlauf R-L (durch<br />
Dekrementierung eines Index j ).<br />
L i j<br />
R<br />
… …<br />
…<br />
! m # m<br />
• L-R stoppt, sobald das erste Element mit a[ i ] " m angetroffen wird.<br />
• R-L stoppt, sobald das erste Element mit a[ j ] ! m angetroffen wird.<br />
• Nach Stop beider Durchläufe $<br />
a[ i ] :=: a[ j ] (Austausch) und setze die Durchläufe fort.<br />
• j < i $ Partitionierung ist beendet.<br />
• Rufe Quicksort rekursiv auf für<br />
! a[L .. j ] falls j > L<br />
"<br />
# a[ i .. R] falls i < R<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Quicksort …<br />
• •zu Beispiel: sortierende Folge: (25, 23, 3, 16, 4, 7, 29, 6)<br />
Partitionierung Partitionierung mit mit m m = = 16 16<br />
25 23 3 16 4 7 29 6<br />
i j<br />
6 23 25 36 23 716<br />
34 16 4 7 16 4 29 23 7 25 29 25 6<br />
i j<br />
6 7 3<br />
i<br />
16<br />
i i<br />
4<br />
ij<br />
23<br />
ij<br />
29<br />
j<br />
25<br />
j<br />
i j<br />
6 7 3 4 16 23 29 25<br />
j i<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
j<br />
5-45<br />
5-46
… Quicksort …<br />
• Wahl des Schwellenwertes muß die Korrektheit und die Effizienz von<br />
Quicksort sicherstellen!<br />
• Korrektheit:<br />
{x i : x i ! m} = $ oder {x j : x j " m} = $ $ Quicksort terminiert nicht! $<br />
Forderung: min(x i ) ! m ! max(x i )<br />
• Effizienz: m sollte nahe am Median liegen!<br />
• Der Median einer Menge von n Elementen ist das Element m, so daß<br />
gleich viele (bis auf eins) Elemente links und rechts von m liegen.<br />
• Wie findet man den Median von n Elementen?<br />
• Der exakte Median kann im schlimmsten Fall in O(n) Zeit bestimmt<br />
werden:<br />
M. Blum, R. W. Floyd, V. R. Pratt, R. L. Rivest, R. E. Tarjan:<br />
Time bounds for selection,<br />
Journal of Computer and System Sciences 7(4), 448 – 461 (1972).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Quicksort …<br />
• Bei Verwendung des exakten Medians werden die zu sortierenden<br />
Elemente in zwei gleich große Teilmengen aufgeteilt $<br />
Zeitaufwand T(n) von Quicksort beträgt<br />
( )<br />
T(n) = 2 !T<br />
und man erhält<br />
T(n) = a · n · log2 n + (T(1) + b) · n – b<br />
n + a !n + b<br />
2<br />
d.h. vom theoretischen Standpunkt aus betrachtet benötigt Quicksort<br />
selbst im schlimmsten Fall nur !(n·log n) Zeit.<br />
• Aber: Die multiplikative Konstante des Algorithmus zum Auffinden<br />
des Medians ist sehr groß $ a ist sehr groß $ Quicksort ist in der<br />
Praxis nicht sehr effizient, falls der exakte Median benutzt wird!<br />
Es lohnt sich nicht, den hohen Aufwand zum Auffinden des exakten<br />
Medians zu treiben!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-47<br />
5-48
… Quicksort<br />
• Techniken zur Bestimmung eines vermuteten Medians, der eine<br />
akzeptable Approximation liefert:<br />
– Ein Arrayelement in fixer Position, z.B. a[(L + R) / 2].<br />
Vorsicht: Benutze kein Element nahe a[L] oder a[R], da dies zu<br />
Ineffizienz im Falle von partiell sortierten Elementen führt.<br />
– Ein Arrayelement in zufällig gewählter Position $ gibt gute<br />
Resultate!<br />
– Den Median von drei oder fünf Elementen an fixen oder zufällig<br />
gewählten Positionen.<br />
– Den Mittelwert des kleinsten und größten Elementes $ erfordert<br />
zusätzlichen Durchlauf zu Beginn, danach kann der Mittelwert für<br />
jedes Teilarray während des vorherigen Aufteilungsprozesses<br />
bestimmt werden.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Quicksort: Rekursive Implementierung<br />
void quicksort(int[] a, int L, int R) {<br />
int m = a[(L + R) / 2];<br />
// min(a[L], … , a[R]) ! m ! max(a[L], … , a[R])<br />
int i = L;<br />
int j = R;<br />
while (i
Implementierung von Quicksort<br />
• Initialer Aufruf:<br />
quicksort(a, 0, a.length - 1);<br />
mit a.length > 1 garantiert, daß L < R in jedem rekursiven Aufruf<br />
von quicksort gilt!<br />
• Quicksort ist ein in place Algorithmus!<br />
• Quicksort ist nicht stabil!<br />
• Für sehr kleine Arrays (n ! 20) ist Quicksort nicht so effizient wie<br />
Insertion Sort.<br />
Quicksort rekursiv $<br />
Quicksort wird sehr häufig für kleine Arrays aufgerufen!<br />
In der Praxis: Modifikation von Quicksort, so daß für kleine Teilarrays<br />
Insertion Sort aufgerufen wird!<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Analyse von Quicksort …<br />
• T(n) sei der von Quicksort benötigte Zeitaufwand $<br />
T(n) = T(k) + T(n – k) + a · n + b (+)<br />
wenn das Array aufgeteilt wird in ein Teilarray mit k Elementen und<br />
ein Teilarray mit n – k Elementen.<br />
– Konstante b: Kosten für den Aufruf von Quicksort für das zu<br />
sortierende Array.<br />
– a · n: Kosten für die Partitionierung.<br />
– T(k) und T(n – k): Kosten der rekursiven Aufrufe für die beiden<br />
Teilarrays.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-51<br />
5-52
… Analyse von Quicksort …<br />
• Beispiel: Aufteilung mit m = 16<br />
25 13 3 16 22 18 29 6<br />
i j<br />
6 13 3 16 22 18 29 25<br />
i j<br />
6 13 3 16 22 18 29 25<br />
j i<br />
$ von Quicksort benötigter Zeitaufwand<br />
T(n) = T(k) + T(n – k – 1) + a · n + b<br />
Diese Rekurrenz hat gleiche asymptotische Lösung wie (+), daher<br />
Beschränkung auf (+).<br />
T(n) = T(k) + T(n – k) + a · n + b (+)<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Analyse von Quicksort …<br />
• Verhalten im besten Fall:<br />
Partitionierung des Arrays in zwei gleich große Teilarrays $<br />
Abschnitt 4.6 $<br />
( )<br />
T(n) = 2 !T n<br />
2<br />
+ a !n + b<br />
T(n) = a · n · log 2 n + (T(1) + b) · n – b<br />
= a · n · log 2 n + g(n) mit g(n) # O(n)<br />
$ Quicksort's Zeitkomplexität im besten Fall ist !(n · log n).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-53<br />
5-54
… Analyse von Quicksort …<br />
• Verhalten im schlimmsten Fall:<br />
Partitionierung des Arrays in ein Teilarray mit n – 1 Elementen und<br />
ein Teilarray mit einem Element $<br />
T(n) = T(1) + T(n – 1) + a · n + b<br />
Substitution:<br />
T(n – 1) = T(1) + T(n – 2) + a · (n – 1) + b<br />
$ T(n) = 2 · T(1) + T(n – 2) + a · n + a · (n – 1) + 2 · b<br />
Wiederholte Substitution ergibt<br />
$ Quicksort's Zeitkomplexität im schlimmsten Fall ist !(n 2 T(n) = n !T(1) + a !<br />
).<br />
n2 + n " 2 + b !(n "1)<br />
2<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Analyse von Quicksort<br />
• Durchschnitt über alle n! Permutationen ist schwer zu ermitteln,<br />
daher Analyse des Verhaltens im typischen Fall, d.h. unter der<br />
Annahme: Für alle k, 1 ! k < n, ist die Wahrscheinlichkeit 1 / (n – 1),<br />
daß das Array aufgeteilt wird in Teilarrays der Größen k und n – k.<br />
• Verhalten im typischen Fall:<br />
Der im Durchschnitt von Quicksort benötigte Zeitaufwand wird<br />
beschrieben durch<br />
T(n) = 1<br />
n !1<br />
n!1<br />
" (T(k ) +T(n ! k )) + a # n + b = 2<br />
n !1<br />
k=1<br />
Lösung durch trial-and-error (Abschnitt 4.5):<br />
T(n) = (ln 4) · a · n · log 2 n + g(n) mit ln 4 # 1.386 und g(n) # O(n)<br />
$ Quicksort's asymptotisches Verhalten ist im typischen Fall nur etwa<br />
40% schlechter als im besten Fall<br />
$ Quicksort's Zeitkomplexität im typischen Fall ist !(n·log n).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
n!1<br />
"<br />
k=1<br />
T(k ) + a # n + b<br />
5-55<br />
5-56
5.8 Merge Sort<br />
• Bisher:<br />
Interne Sortieralgorithmen mit direktem Zugriff auf jedes Element!<br />
• Annahme bei der Algorithmenanalyse: Zugriff auf a[i] ist primitive<br />
Operation mit konstanten Kosten, d.h. unabhängig von i und n.<br />
• Annahme gilt nicht für Sekundärspeichermedien wie Festplatten oder<br />
Magnetbänder!<br />
• Externes Sortieren:<br />
Zu sortierende Elemente sind in einer sequentiellen Datei (File)<br />
gespeichert.<br />
– Zu jedem Zeitpunkt kann man über einen Zeiger nur auf genau ein<br />
Element zugreifen.<br />
– Zugriff auf andere Elemente erfordert Neupositionierung des<br />
Zeigers in der Datei. Zeiger kann nur in einer Richtung (vorwärts)<br />
oder in beide Richtungen (vorwärts und rückwärts) wandern.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Externer Merge Sort …<br />
• Annahme: Zeit zur Repositionierung des Zeigers ist proportional zur<br />
zurückgelegten Distanz.<br />
• Annahme<br />
– begünstigt Algorithmen, die nur benachbarte Elemente verarbeiten<br />
(vergleichen, austauschen).<br />
– benachteiligt Algorithmen, die wahlfreien Zugriff auf die Elemente<br />
benötigen (z.B. Quicksort).<br />
• f k bezeichne das k-te Element, das in Datei f gespeichert ist.<br />
• Ein Lauf (Run) ist eine sortierte Teilfolge von Elementen f i , f i+1 , …, f j ,<br />
die aufeinanderfolgend in Datei f gespeichert sind und für die<br />
f k ! f k+1 für alle k mit i ! k ! j – 1 gilt.<br />
• Verfügbarer Puffer im Hauptspeicher mit Kapazität m Elemente<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-57<br />
5-58
… Externer Merge Sort …<br />
• Externer Sortieralgorithmus, basierend auf dem Prinzip des Merge Sort:<br />
– Initiale Phase:<br />
Erzeuge im Hauptspeicher initiale Läufe der Länge m (evtl. weniger<br />
Elemente im letzten Lauf) und speichere die Läufe in Datei f.<br />
– Kopierphase (Copy):<br />
Kopiere die Hälfte der Läufe von f in Datei g, die andere Hälfte in<br />
Datei h.<br />
– Verschmelzungsphase (Merge):<br />
Verschmelze jeden Lauf von g mit genau einem Lauf von h und<br />
schreibe den resultierenden Lauf nach f.<br />
– Wiederhole Kopier- und Verschmelzungsphase, bis f nur noch einen<br />
einzigen Lauf enthält.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Externer Merge Sort<br />
• Realisierung der initialen Phase:<br />
– Lese aus Eingabedatei e die m Elemente e r·m+1 , e r·m+2 , …, e r·m+m in<br />
den Puffer (r = 0, 1, …).<br />
– Sortiere die Elemente im Puffer mit einem internen Sortierverfahren<br />
(z.B. Quicksort).<br />
– Schreibe den so erzeugten r-ten Lauf in die Datei f, die den gleichen<br />
physischen Speicherbereich belegen kann wie die ursprüngliche<br />
Datei e.<br />
$ f ist partiell sortiert: " k, r · m + 1 ! k < r · m + m: f k ! f k+1<br />
• Jeder Copy-Merge-Zyklus halbiert die Anzahl Läufe:<br />
copy<br />
g: s/2 Läufe<br />
f: s Läufe f: s/2 Läufe<br />
h: s/2 Läufe<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
merge<br />
Nach &log 2 (n/m)' Copy-Merge-Zyklen besteht f aus einem einzigen<br />
Lauf, der die sortierte Folge aller Elemente darstellt.<br />
5-59<br />
5-60
Interner Merge Sort …<br />
void mergeSort(int[] a, int L, int R) {<br />
int M = (L + R) / 2;<br />
if (L < M)<br />
mergeSort(a, L, M);<br />
if (M + 1 < R)<br />
mergeSort(a, M + 1, R);<br />
merge(a, L, M, R);<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Interner Merge Sort …<br />
void merge(int[] a, int L, int M, int R) {<br />
int[] b = new int[R–L+1];<br />
int i = L;<br />
int j = M + 1;<br />
int k;<br />
for (k = L; k M) || ((j
… Interner Merge Sort …<br />
• Interner Merge Sort ist kein in place Algorithmus, da er ein<br />
zusätzliches Array benötigt.<br />
• Interner Merge Sort ist stabil!<br />
• Interner Merge Sort ist ein divide & conquer Algorithmus:<br />
7<br />
7 5<br />
5 7<br />
7 5 3 2<br />
5<br />
2 3 5 7<br />
7 5 3 2 1 8 6 4<br />
divide<br />
3 2<br />
divide<br />
divide<br />
1 2 3 4 5 6 7 8<br />
1 8<br />
1 8 6 4<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Interner Merge Sort<br />
• Zeitaufwand<br />
( )<br />
T(n) = 2 !T n<br />
2<br />
6 4<br />
3 2 1 8 6 4<br />
merge<br />
2 3<br />
1 8 4 6<br />
merge<br />
merge<br />
1 4 6 8<br />
+ a !n + b<br />
Abschnitt 4.6 $<br />
T(n) = a · n · log 2 n + (T(1) + b) · n – b<br />
= a · n · log 2 n + g(n) mit g(n) # O(n)<br />
$ Zeitkomplexität von Merge Sort ist in jedem Fall !(n · log n).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-63<br />
5-64
5.9 Ist es möglich, in linearer Zeit zu sortieren?<br />
• Untere Schranke "(n · log n) gilt für Sortieralgorithmen, die Information<br />
über die Anordnung der Elemente nur über zweiwertige Fragen und<br />
nichts anderem beziehen!<br />
• Diese untere Schranke gilt nicht unbedingt für andere Situationen!<br />
• Beispiel 1: Sortieren einer Permutation<br />
Sortieren einer Permutation der ganzen Zahlen von 1 bis n in !(n) Zeit<br />
durch Speichern des Elementes i im Arrayelement mit Index i – 1.<br />
• Beispiel 2: Counting Sort<br />
Annahme: Jedes der n zu sortierenden Elemente ist eine ganze Zahl<br />
zwischen 0 und k – 1.<br />
Idee: Bestimme für jedes Element x die Anzahl der Elemente ! x $<br />
Jedes Element kann direkt an seine korrekte Position im Ausgabearray<br />
gebracht werden.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Counting Sort …<br />
void countingSort(int[] a, int k) {<br />
int[] b = new int[a.length];<br />
int[] c = new int[k];<br />
int i;<br />
for (i = 0; i < k; i++)<br />
c[i] = 0;<br />
for (i = 0; i < a.length; i++)<br />
c[a[i]] = c[a[i]] + 1;<br />
// c[i] enthält jetzt die Anzahl Elemente = i<br />
for (i = 1; i < k; i++)<br />
c[i] = c[i] + c[i - 1];<br />
// c[i] enthält jetzt die Anzahl Elemente ! i<br />
for (i = a.length - 1; i >= 0; i--) {<br />
b[c[a[i]] - 1] = a[i];<br />
c[a[i]] = c[a[i]] - 1;<br />
}<br />
for (i = 0; i < a.length; i++)<br />
a[i] = b[i];<br />
}<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-65<br />
5-66
… Counting Sort<br />
• Counting Sort ist kein in place Algorithmus.<br />
• Counting Sort ist stabil!<br />
• Zeitkomplexität von Counting Sort ist !(n + k).<br />
• Counting Sort wird in der Praxis eingesetzt, wenn k # O(n) $<br />
Zeitkomplexität !(n).<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Sortieren in linearer Zeit?<br />
• Widersprechen Beispiele 1 & 2 der unteren Schranke "(n · log n)?<br />
• Nein, da das Sammeln von Informationen durch das Stellen von (in<br />
den Zugriffen auf die einzelnen Arrayelemente versteckten) Fragen<br />
erfolgt, die mächtiger sind als binäre Fragen:<br />
– n-wertige Fragen im Beispiel 1,<br />
– k-wertige Fragen im Beispiel 2.<br />
• Jede k-wertige Frage entspricht log 2 k binären Fragen. Wird dieser<br />
Umtauschfaktor berücksichtigt, so sind die Zeitkomplexitäten der<br />
obigen Algorithmen<br />
– !(n · log n) im Beispiel 1 und<br />
– !(n · log k + k) im Beispiel 2.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-67<br />
5-68
Radix Sort<br />
• Beispiel 3: Radix Sort<br />
Annahme: Die zu sortierenden Elemente sind ganze Zahlen bestehend<br />
aus höchstens d Ziffern, wobei die Ziffernpositionen von 1 bis d durchnumeriert<br />
sind. Die am weitesten rechts stehende Ziffer hat Position 1,<br />
die am weitesten links stehende Ziffer Position d. Die Basis des<br />
Zahlensystems sei b, d.h. 0 ! z < b für jede Ziffer z.<br />
Implementierung:<br />
void radixSort(int[] a, int d) {<br />
for (int i = 1; i
… Sortiernetzwerke …<br />
• Komparatoren sind Prozessoren, die Werte auf zwei Eingabekanälen<br />
vergleichen und sie an zwei Ausgabekanäle weitergeben, so daß der<br />
kleinere Wert auf dem oberen Ausgabekanal, der größere Wert auf<br />
dem unteren Ausgabekanal erscheint.<br />
x<br />
y<br />
Komparator<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
min(x, y)<br />
max(x, y)<br />
• Anordnung von Komparatoren innerhalb eines Netzwerks, daß aus n<br />
waagerecht verlaufenden Kanälen besteht. Vertikale Verbindungen<br />
zwischen den Kanälen repräsentieren Komparatoren. Zahlen<br />
repräsentieren Eingabewerte, die durch das Netzwerk wandern und<br />
miteinander verglichen werden:<br />
4<br />
1<br />
3<br />
2<br />
… Sortiernetzwerke …<br />
1<br />
4<br />
3<br />
4 2<br />
1<br />
3<br />
2<br />
4 4<br />
• Ein Netzwerk von Komparatoren ist ein Sortiernetzwerk, wenn es jede<br />
Eingabekonfiguration sortiert.<br />
• Eingabekonfiguration besteht aus verschiedenen Elementen $<br />
oBdA: Eingabekonfigurationen sind Permutationen der Zahlen<br />
1, 2, … , n<br />
• Netzwerk, daß duplikatfreie Eingabekonfigurationen sortiert, sortiert<br />
auch Konfigurationen mit Duplikaten.<br />
• (4, 1, 3, 2) wird nicht korrekt sortiert:<br />
$ kein Sortiernetzwerk!<br />
4<br />
1<br />
3<br />
2<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
1<br />
4<br />
3<br />
4 2<br />
1<br />
3<br />
2<br />
4 4<br />
5-71<br />
5-72
… Sortiernetzwerke …<br />
• Wieviele Komparatoren werden benötigt, wie sollten sie plaziert werden?<br />
• Sortiernetzwerk für 4 Elemente:<br />
k 1 k 2 k 3 k 4 k 5<br />
• Wie kann man feststellen, ob ein gegebenes Netzwerk korrekt sortiert?<br />
– Ausschöpfendes Testen ist allenfalls für kleine Netzwerke möglich.<br />
– Netzwerke mit regelmäßiger Struktur erlauben gewöhnlich einen<br />
einfacheren Korrektheitsbeweis.<br />
• Beispiel:<br />
k 1 , k 2 und k 3 plazieren das kleinste Element auf dem obersten Kanal.<br />
k 1 , k 2 und k 4 plazieren das größte Element auf dem untersten Kanal.<br />
Die mittleren beiden Elemente verbleiben auf den beiden mittleren<br />
Kanälen und werden durch k 5 in die richtige Reihenfolge gebracht.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Sortiernetzwerke …<br />
• Entwurfsprinzipien für die Entwicklung großer Sortiernetzwerke?<br />
• Abbildung eines Sortieralgorithmus, der für sequentielle Maschine<br />
entwickelt wurde, in ein Sortiernetzwerk im allgemeinen nicht direkt<br />
möglich, da Netzwerke ein eingeschränkteres Berechnungsmodell<br />
darstellen:<br />
Sequentieller Sortieralgorithmus führt Vergleiche in Abhängigkeit<br />
vom Ausgang vorheriger Vergleiche durch ,<br />
Sortiernetzwerk macht immer die gleichen Vergleiche für alle<br />
Eingabekonfigurationen.<br />
• Aber: gleiche grundlegende Designprinzipien, die beim Entwurf<br />
sequentieller Algorithmen eingesetzt werden, treffen auch auf<br />
parallele Algorithmen zu.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-73<br />
5-74
… Sortiernetzwerke …<br />
• Divide & Conquer:<br />
Plaziere zwei Sortiernetzwerke für jeweils n Kanäle übereinander und<br />
kombiniere sie in ein Sortiernetzwerk mit 2·n Kanälen durch<br />
Hinzufügen eines Merge-Netzwerks, daß ihre Ausgaben verschmilzt.<br />
Die rigide Struktur von Komparatornetzwerken kompliziert die<br />
Kombination von Netzwerken!<br />
• Inkrementeller Algorithmus:<br />
Plaziere einen n-ten Kanal unter ein Sortiernetzwerk mit n – 1 Kanälen<br />
und schalte diesem Netzwerk eine Kaskade von Komparatoren vor<br />
oder nach, die diesen Kanal in das existierende Netzwerk einbinden.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
… Sortiernetzwerke …<br />
• Insertion Sort:<br />
Elemente auf den oberen n – 1 Kanälen bereits sortiert $ Element<br />
auf dem untersten Kanal wandert nach oben in die richtige Position:<br />
Sortiernetzwerk<br />
für<br />
n–1 Elemente<br />
• Selection Sort:<br />
Das größte Element wandert zuerst nach unten, dann werden die<br />
restlichen Elemente sortiert:<br />
Sortiernetzwerk<br />
für<br />
n–1 Elemente<br />
Induktion<br />
Induktion<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-75<br />
5-76
… Sortiernetzwerke<br />
• Komparatoren können zur Minimierung der Anzahl Berechnungsstufen<br />
an ihren Kanälen entlang verschoben werden unter der Voraussetzung,<br />
daß die Topologie des Netzwerkes sich nicht ändert.<br />
Diese Kompression reduziert sowohl Insertion Sort als auch Selection<br />
Sort auf das folgende Netzwerk:<br />
!(n 2 ) Komparatoren, Zeitkomplexität 2 · n – 3 # !(n)<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
Zusammenfassung<br />
• <strong>Sequentielles</strong> <strong>Suchen</strong> in Array in !(n) Zeit<br />
• Binäres <strong>Suchen</strong> in geordnetem Array in !(log n) Zeit<br />
• Sortierproblem<br />
• Verschiedene Typen von Sortieralgorithmen<br />
• Einfache Algorithmen mit Zeitaufwand !(n 2 ):<br />
Insertion Sort, Selection Sort<br />
• Untere Schranke "(n · log n)<br />
• Quicksort mit durchschnittlichem Zeitaufwand !(n · log n),<br />
im schlimmsten Fall !(n 2 )<br />
• Interner Merge Sort mit Zeitaufwand !(n · log n) im schlimmsten Fall<br />
• Sortieren in linearer Zeit? Counting Sort, Radix Sort<br />
• Ausblick:<br />
Heapsort ist ein nicht stabiler, in place Sortieralgorithmus mit<br />
Zeitaufwand !(n · log n) im schlimmsten Fall.<br />
© Klaus Hinrichs Informatik II – <strong>Suchen</strong> und Sortieren<br />
5-77<br />
5-78