13SS_6b_PG1_Algorithmen_Datenstrukturen_Suchen_Sortieren_Stud_01_21.pdf
13SS_6b_PG1_Algorithmen_Datenstrukturen_Suchen_Sortieren_Stud_01_21.pdf
13SS_6b_PG1_Algorithmen_Datenstrukturen_Suchen_Sortieren_Stud_01_21.pdf
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
<strong>Algorithmen</strong> und <strong>Datenstrukturen</strong> (Beispiele in C++)<br />
Reiner Nitsch<br />
8471<br />
reiner.nitsch@h-da.de
Such-<strong>Algorithmen</strong> – Lineare Suche<br />
Prinzip der linearen Suche:<br />
‣Betrachte jedes Element im Suchbereich<br />
‣Vergleiche jedes Element im Suchbereich mit dem Suchwert<br />
‣Wenn gefunden (Suchtreffer), gib Index oder Zeiger auf Suchtreffer zurück<br />
‣Wenn nicht gefunden (Suchfehler), gib Sentinel zurück.<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
int* find( int* pfirst, int* plast, int val ) {<br />
while( pfirst
Such-<strong>Algorithmen</strong> – Binäre Suche<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Nachteil der linearen Suche: langsam, weil Suche in kleinen Schritten<br />
Besser: Suche in großen Schritten<br />
Frage: Wie errät man eine Zufalls-Zahl zwischen 0 und 100 am schnellsten?<br />
2. Version des Suchalgorithmus: Binäre Suche<br />
Algorithmischer Kern<br />
Rateversuch: Mittlere Zahl aus (sortiertem) Suchbereichs<br />
wenn gleich: fertig<br />
sonst: links oder rechts weitersuchen<br />
int* binarySearchI( int* pfirst, int* plast, int val ) {<br />
//Binäre Suche (iterativ) im Bereich [pfirst, plast);<br />
// Vorbedingung: sortierte Elemente in Bezug auf operator<<br />
}<br />
int* pend = plast;<br />
while (pfirst
Aufwand von <strong>Algorithmen</strong> (Komplexität)<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Kriterien sind u.a.<br />
Speicheraufwand<br />
‣ für Programm/Algorithmus<br />
‣ für Daten<br />
Zeitaufwand<br />
‣ für Aufruf und Initialisierungen<br />
‣ für Wiederholungen<br />
statisch<br />
dynamisch, d.h. abhängig von Datenmenge<br />
statisch<br />
dynamisch<br />
Speicherkomplexität<br />
Zeitkomplexität<br />
Eine präzise detailreiche Bestimmung der Aufwände wird i.A nicht durchgeführt, weil dies<br />
‣ mathematisch oft nicht handhabbar ist<br />
‣ uninteressant ist: für Vergleichszwecke reicht auch ger. Detailierungsgrad<br />
Vereinfachungen (Abstraktionen) bei der Ermittlung des Zeitaufwandes<br />
Der tatsächliche Zeitaufwand ist immer prozessorabhängig.<br />
Um davon unabhängig zu werden, macht man folgende Vereinfachungen:<br />
‣ Jede Anweisung (Schleifen ausgenommen) benötigt den Zeitaufwand 1<br />
‣ Bei Wiederholungen/Schleifen sind wiederholten Anweisungen mit der Anzahl der Wiederholungen zu<br />
gewichten, die meist von der Anzahl n der Eingabedaten bestimmt ist.<br />
20.06.2<strong>01</strong>3 5
Aufwand von <strong>Algorithmen</strong> - Abstraktionen<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Wenn die Algorithmuslaufzeit T(n) nicht für alle Eingaben derselben Länge n gleich<br />
ist, sind folgende Grenzfälle interessant:<br />
der beste Fall (best case) T best<br />
der schlimmste Fall (worst case) T worst<br />
das Durchschnittsverhalten (average case) T avg<br />
O-Notation (auch Landau-Notation) der Laufzeitfunktion<br />
ist ein mathematisches Verfahren zur Einordnung der Komplexität von Funktionen für<br />
großes n.<br />
benennt lediglich aus einer Klasse gleich schnell wachsender Funktionen den einfachsten<br />
Repräsentanten als obere Schranke (Supremum). Meist reicht diese Abschätzung der<br />
Größenordnung, weil <strong>Algorithmen</strong> sich schon hier unterscheiden.<br />
gibt ein Maß für die Anzahl der Elementarschritte in Abhängigkeit von der Anzahl n der<br />
Eingabedaten an.<br />
20.06.2<strong>01</strong>3 6
Such-<strong>Algorithmen</strong> – Binäre Suche<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
1<br />
1<br />
1<br />
1<br />
1<br />
Lineare Suche<br />
1<br />
1<br />
int* find( int* pfirst, int* plast, int val ) {<br />
while( pfirst
Komplexität der binären Suche<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Aufwandsabschätzung für binäre Suche<br />
• Jede Iteration benötigt die Zeit 3<br />
• Jede Iteration halbiert den Suchbereich<br />
Nach dem 1. Halbieren enthält Suchbereich noch n/2 = n/2 1 Elemente<br />
Nach dem 2. Halbieren enthält Suchbereich noch n/4 = n/2 2 Elemente<br />
…<br />
Nach dem R-ten Halbieren enthält Suchbereich noch n/2 R Elemente<br />
• Worst case: Suche endet, wenn Suchbereich nur noch 1 Element enthält!<br />
n<br />
2 R = 1 R = log 2(n) Wiederholungen Zeitkomplexität T(n) = R(n)·3+2= log 2 (n) · 3 + 2<br />
d.h. die Zeitkomplexität wächst logarithmisch mit n<br />
Dies bringt man abkürzend durch die "Big-O"-Notation zum Ausdruck: Zeitkomplexität = O( log n )<br />
Ergebnis: In sortierten Reihen kann wesentlich schneller gesucht werden!<br />
Beispiel: Laufzeitvergleich<br />
lineare ↔ binäre Suche<br />
(gemessene Werte:<br />
Intel X86 Prozessor, 1,66 GHz,<br />
Debug-Konfiguration)<br />
n O(n) O(log n)<br />
1000 2,6 us 0,1 us = 0,033 us • 3<br />
10 6 2,6 ms 0,2 us = 0,033 us • 6<br />
10 9 2,6 s 0,3 us = 0,033 us • 9<br />
20.06.2<strong>01</strong>3 8
Beispiele zur Schätzung der Zeitkomplexität<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Laufzeit<br />
Anweisung Zeitkomplexität T(n) Big-O Typische <strong>Algorithmen</strong><br />
x=x+1;<br />
for (int i=1; i
Weitere Beispiele für O-Notation der Algorithmuslaufzeit<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Notation Bedeutung Anschauliche Erklärung Beispiele für Laufzeiten<br />
T(n) є O(1)<br />
ist konstant<br />
überschreitet einen konstanten Wert nicht<br />
(unabhängig vom Wert des Arguments).<br />
Nachschlagen des x-ten Elementes in einem<br />
Feld.<br />
T(n) є O(log n)<br />
wächst<br />
logarithmisch<br />
wächst ca. um einen konstanten Betrag,<br />
wenn sich das Argument verzehnfacht.<br />
Binäre Suche im sortierten Feld mit n<br />
Einträgen<br />
T(n) є O(√n)<br />
wächst wie die<br />
Wurzelfunktion<br />
wächst ungefähr auf das Doppelte, wenn<br />
sich das Argument vervierfacht<br />
naiver Primzahltest mittels Teilen durch<br />
jede Zahl ≤n<br />
T(n) є O(n)<br />
wächst linear<br />
wächst ungefähr auf das Doppelte, wenn<br />
sich das Argument verdoppelt.<br />
Suche im unsortierten Feld mit Einträgen<br />
(Bsp. Lineare Suche)<br />
T(n) є O(nlog n)<br />
hat super-lineares<br />
Wachstum<br />
Fortgeschrittenere <strong>Algorithmen</strong> zum<br />
<strong>Sortieren</strong> von Zahlen Mergesort, Heapsort<br />
T(n) є O(n 2 )<br />
wächst<br />
quadratisch<br />
wächst ungefähr auf das Vierfache, wenn<br />
sich das Argument verdoppelt<br />
Einfache <strong>Algorithmen</strong> zum <strong>Sortieren</strong> von<br />
Zahlen Selectionsort<br />
T(n) є O(n k )<br />
wächst<br />
polynomiell<br />
wächst ungefähr auf das Doppelte, wenn<br />
sich das Argument um eins erhöht<br />
Zahlenschloßprojekt T(n)=O(n 3 )<br />
T(n) є O(n!)<br />
wächst faktoriell<br />
wächst ungefähr um das n-fache, wenn<br />
sich das Argument um eins erhöht.<br />
Problem des Handlungsreisenden<br />
20.06.2<strong>01</strong>3 10
Warum ist die Zeitkomplexität eines Algorithmus so wichtig?<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Häufig ist die erste Idee, die HW zu beschleunigen.<br />
Aber: der Geschwindigkeitsvorteil ist dabei auf konstanten Faktor beschränkt<br />
Bessere HW ist zudem teuer, bzw. stößt an technische Grenzen<br />
Ein schneller Algorithmus auf einer langsamen Maschine wird immer schneller<br />
sein als ein langsamer Algorithmus auf einer schnellen Maschine!<br />
Supercomputers are for people too rich and<br />
too stupid to design efficient algorithms<br />
(Steven Skiena)<br />
20.06.2<strong>01</strong>3 11
<strong>Sortieren</strong><br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Für ein Feld von n Objekten gibt es n! Permutationen<br />
<strong>Sortieren</strong> ist ein Vorgang, der durch (möglichst wenige) paarweise Vergleiche von<br />
Objekten eine dieser Permutationen herausfiltert, in der die Objekte einer<br />
bestimmten Ordnungsrelation (z.B. größer, kleiner, …) genügen.<br />
Stabile Sortierung<br />
Beispiel:<br />
7<br />
2<br />
2<br />
3<br />
5<br />
5<br />
3<br />
7<br />
unsortiert sortiert<br />
Kommt ein Schlüsselwert<br />
mehrfach vor (Duplikate) ist<br />
die Sortierung nicht eindeutig:<br />
Ein Sortierverfahren, bei dem die<br />
Reihenfolge von Schlüsselduplikaten<br />
nach dem <strong>Sortieren</strong> unverändert ist,<br />
bezeichnet man als<br />
"Stabiles Sortierverfahren"<br />
1<br />
3<br />
3<br />
7<br />
7<br />
3<br />
1<br />
3<br />
1<br />
3<br />
3<br />
7<br />
sortiert unsortiert stabil sortiert<br />
Schlüsselwerte<br />
20.06.2<strong>01</strong>3 12
Sortierverfahren - Klassifizierungskriterien<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Zeitverbrauch<br />
‣ Anzahl Schlüsselvergleiche<br />
‣ Anzahl Vertauschungen<br />
‣ Sensibilität bezüglich Eingabeverteilung<br />
Zeitkomplexität<br />
Speicherplatzverbrauch (Programme u. Daten)<br />
Speicherkomplexität<br />
‣ Speicherplatzbedarf am geringsten für <strong>Sortieren</strong> am Ort ("in place" oder "in situ")<br />
Stabile Sortierverfahren, ändern die Reihenfolge von Duplikaten beim <strong>Sortieren</strong> nicht.<br />
Wie schnell kann man sortieren?<br />
Voraussetzung für jedes <strong>Sortieren</strong>: Auf den zu sortierenden Objekten muss eine Ordnung für<br />
die Schlüsselwerte definiert sein.<br />
Untere Komplexitätsschranke für Sortierverfahren:<br />
Satz: Jedes vergleichsbasierte Sortierverfahren für N Elemente benötigt im<br />
Mittel und im schlechtesten Fall eine Laufzeit von wenigstens<br />
T(N)O(N·log N) Vergleiche<br />
20.06.2<strong>01</strong>3 13
Sortierverfahren - Klassifizierung von Sortiertechniken<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
<strong>Sortieren</strong> durch<br />
L1<br />
Min. Element von L2<br />
1) Auswählen<br />
2) Einfügen<br />
sortiert<br />
sortiert<br />
3) Austauschen Elementare Sortierverfahren<br />
4) Zerlegen<br />
K i >K<br />
K<br />
K j
<strong>Sortieren</strong> durch Austauschen (exchange sort, bubble sort)<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Austauschen<br />
lokal<br />
Idee:<br />
Beginnend am Anfang der unsortierten<br />
Teilreihe werden jeweils Elementpaare<br />
gebildet.<br />
Die Elemente eines Paares werden<br />
verglichen und dann getauscht, wenn das<br />
größere Element näher am Anfang der<br />
Reihe liegt.<br />
Nach N-1 Schritten ist der 1. Sortierlauf<br />
beendet und das größte Element zum Ende<br />
der Reihe wie eine Blase "aufgestiegen" (<br />
Bubble Sort). Es bildet dort das 1. Element<br />
der teilsortierten Reihe.<br />
Der Vorgang wird mit der um ein Element<br />
kleineren unsortierten Teilreihe wiederholt.<br />
Nach N-1 solchen Sortierläufen ist der<br />
Sortiervorgang abgeschlossen.<br />
first 420 97 97 97 97<br />
97 420 420 420 420<br />
420 420 420 3<strong>01</strong> 3<strong>01</strong><br />
3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 420 35<br />
35 35 35 35 420<br />
last … … … … …<br />
teilsortierte Reihe unsortierte Reihe<br />
first 97 97 97 97<br />
420 420 3<strong>01</strong> 3<strong>01</strong><br />
3<strong>01</strong> 3<strong>01</strong> 420 35<br />
35 35 35 420<br />
420 420 420 420<br />
last … … … …<br />
Ist der Sortiervorgang stabil?<br />
ja<br />
20.06.2<strong>01</strong>3 15
Implementierung von Bubble-Sort<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
/* BubbleSort - Sorts a subsequence [pfirst,plast)<br />
@param pfirst pointer to first element of subsequence<br />
@param plast pointer to one-past-the-end element of the subsequence */<br />
// version 1 (with pointer)<br />
void bubbleSort( int* pfirst, int* plast ) {<br />
}<br />
for ( int* plastu=plast-1; plastu>pfirst; --plastu )<br />
for ( int* pp=pfirst ; pp
Aufwandsabschätzung für Bubble-Sort<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Zeitkomplexität<br />
<br />
<br />
AnzVgl . N 1 ( N 2) ... 1<br />
12 ... ( N 1)<br />
<br />
N 1<br />
k 1<br />
N N 2<br />
N N<br />
( 1)<br />
<br />
2 2 2<br />
<br />
2<br />
N N<br />
Anzahl Vertauschungen <br />
4 4<br />
<br />
<br />
<br />
<br />
<br />
Im Mittel halb so viele wie Vergleiche.<br />
Zusammenfassung<br />
k<br />
first 97 97 97 97<br />
teilsortierte Reihe<br />
in-place<br />
stabil<br />
Vertauschungen: O(N 2 ) im Mittel und im worst case; keine im best case.<br />
Vergleiche (Ver.2): O(N 2 ) im Mittel und im worst case; O(N) im best case.<br />
Aufwand (Anzahl Vergleiche) ist unabhängig von Eingabeverteilung.<br />
Wird in der Praxis kaum eingesetzt.<br />
420 420 3<strong>01</strong> 3<strong>01</strong><br />
3<strong>01</strong> 3<strong>01</strong> 420 35<br />
35 35 35 420<br />
420 420 420 420<br />
last … … … …<br />
unsortierte Reihe<br />
first 97 97 97 97<br />
420 420 3<strong>01</strong> 3<strong>01</strong><br />
3<strong>01</strong> 3<strong>01</strong> 420 35<br />
35 35 35 420<br />
420 420 420 420<br />
last … … … …<br />
20.06.2<strong>01</strong>3 17
<strong>Sortieren</strong> durch Auswahl (selection sort, MinSort, ExchangeSort)<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Idee:<br />
Auswahl des kleinsten Elementes im<br />
unsortierten Teil (L2) der Reihe<br />
Austausch mit dem ersten Element der<br />
unsortierten Teilreihe<br />
Die teilsortierte Reihe (L1) ist danach<br />
um 1 Element gewachsen. Die<br />
unsortierte Reihe enthält 1 Element<br />
weniger.<br />
Nach N solchen Durchläufen ist die<br />
Reihe sortiert.<br />
first 420 35 35 35 35<br />
420 420 97 97 97<br />
97 97 420 3<strong>01</strong> 3<strong>01</strong><br />
3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 420 420<br />
35 420 420 420 420<br />
last … … … … …<br />
teilsortierte Reihe unsortierte Reihe<br />
Auswählen<br />
L1 Min. Element von L2<br />
first<br />
firstu<br />
min<br />
last<br />
Ist der Sortiervorgang stabil?<br />
Beispiel: s. Abb. ( Key 420 )<br />
20.06.2<strong>01</strong>3 18
<strong>Sortieren</strong> durch Auswahl - Komplexität und Eigenschaften<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
// Algorithmus in Pseudocode<br />
prozedur selectionSort( a, first, last )<br />
[ a[first], a[last] ) : Bereich sortierbarer Elemente<br />
Variable: firstu:=first Position d. 1. Elem. der unsort. Reihe<br />
wiederhole<br />
Variable: min:= firstu<br />
für jede Position pos von firstu+1 bis last-1 wiederhole<br />
falls a[pos]
<strong>Sortieren</strong> durch Einfügen (insertion sort)<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Idee: Vorgehen wie beim <strong>Sortieren</strong> eines<br />
Kartenspiels.<br />
1. Starte mit oberster Karte den sortierten<br />
Stapel<br />
2. Nimm die jeweils nächste Karte vom<br />
unsort. Stapel<br />
3. Füge sie an der richtigen Stelle im<br />
sortierten Stapel ein<br />
In einem Array müssen die größeren<br />
Elemente um 1 Indexposition weiter<br />
rücken, um dem kleineren Einfügeelement<br />
Platz zu machen.<br />
Mit jedem Einfügeschritt wird der<br />
sortierte Stapel um 1 ein Element größer.<br />
Nach N-1 solchen Einfügeschritten ist der<br />
Sortiervorgang abgeschlossen.<br />
first 420 97 97 35 35<br />
97 420 420 97 97<br />
420 420 420 420 3<strong>01</strong><br />
35 35 35 420 420<br />
3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 420<br />
last … … … … …<br />
teilsortierte Reihe<br />
unsortierte Reihe<br />
prozedur insertionSort( a, first, last )<br />
[ a[first], a[last] ) : Bereich sortierbarer Elemente<br />
pos: mögliche Einfügeposition<br />
posC: Position des nächsten Einfügekandidaten<br />
valC: Wert des nächsten Einfügekandidaten<br />
für jedes Element von posC=first+1 bis last-1 wiederhole<br />
pos = posC<br />
6 5 4 3 2 1 valC<br />
valC := a[posC]<br />
solange first
<strong>Sortieren</strong> durch Einfügen - Komplexität und Eigenschaften<br />
FB Informatik<br />
Prof. Dr. R.Nitsch<br />
Worst Case bei invers sortierter Reihe<br />
Max. Anzahl der Vergleiche:<br />
12 ... ( N 1)<br />
<br />
( 1)<br />
<br />
2 2 2<br />
2<br />
N N N N<br />
N 1<br />
<br />
k 1<br />
Max. Anzahl der Verschiebungen:<br />
12 ... ( N 1) N( N 1)/ 2<br />
k<br />
first 420 97 97 35 35<br />
97 420 420 97 97<br />
420 420 420 420 3<strong>01</strong><br />
35 35 35 420 420<br />
3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 3<strong>01</strong> 420<br />
last … … … … …<br />
teilsortierte Reihe unsortierte Reihe<br />
Best Case bei sortierter Reihe<br />
Min. Anzahl der Vergleiche: 1 1 ... 1N<br />
1<br />
Min. Anzahl der Verschiebungen:<br />
0<br />
Vorteilhaft bei fast sortierten Reihen:<br />
In-place-Verfahren, stabil<br />
20.06.2<strong>01</strong>3 21