Programmieren, Algorithmen und Datenstrukturen II 5. Effizientes Sortieren und Suchen – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 1 Übersicht 1. Ziele des Kapitels 2. Asymptotische Schranken 3. Wiederholung bekannter Sortieralgorithmen 4. Heap Sort 5. Quicksort 6. Analyse rekursiver Algorithmen 7. Einführung Suchen 8. Hashing 9. Assoziative STL-Container – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 2 1. Ziele des Kapitels Kapitel 1 2 4 3 5 6 7 8 C++ und SW Engineering Algorithmen und Datenstrukturen Ein- / Ausgabe mit Dateien, Ausnahmebehandlung Vererbung Abstrakte Datentypen + Klassen-Templates Überladen von Operatoren Sortieren und Suchen Bäume Graphen Allgemeine Lösungsverfahren – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 3 1. Ziele des Kapitels Bisher haben wir fast ausschließlich Sortieralgorithmen mit quadratischer Laufzeit kennen gelernt. In diesem Abschnitt betrachten wir bessere Algorithmen, welche i.d.R. ● rekursiv arbeiten ● und nach dem Divide-and-conquer-Paradigma (Teile und Herrsche) arbeiten Darüber hinaus betrachten wir die beim Programmieren immer wieder kehrende Problematik, Daten zu speichern, um sie später noch einmal wieder benutzen zu können. Eine Technik, dies zu tun ist Hashing. Zunächst werden ein paar wichtige Grundlagen aus dem 1. Semester wiederholt. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 4 2. Asymptotische Schranken [Wiederholung aus dem 1. Semester] In der Algorithmenanalyse interessiert man sich meistens für den Worst-Case (WC): also die schlechteste Laufzeit bei Eingabegrößen (obere Schranke für die Laufzeit). Der mittlere Fall (AvgCase) ist häufig nur sehr schwer zu bestimmen und ist zudem oftmals auch nicht besser als der Worst-Case. Asymptotische Schranken ● sind ein abstraktes Modell, das es erlaubt die Effizienz von verschiedenen Algorithmen zu vergleichen. ● Die Effizienz wird dabei ausgedrückt durch eine Laufzeit-Funktion, welche in Abhängigkeit von der Eingabe-Größe n, die Anzahl der benötigten Einzelschritte (Statements) vorhersagt, z.B. f(n) = n2 + 20 n – 7 ● Zusätzlich beschränkt sich eine asymptotische Schranke auf den Teil der Funktion, der den größten Betrag liefert (hier n2). Man macht also eine Abschätzung in der O-Notation. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 5 2. Asymptotische Schranken 2.1 Formale Definition der O-Notation: Es ergeben sich folgende formalen Rechenregeln zur Vereinfachung: ● f(n) = O(f(n)) ● O(c f(n)) = O(f(n)) ● O(f(n)) + O(g(n)) = O(f(n) + g(n)) = O(max(f(n),g(n))) ● O(f(n)) O(g(n)) = O(f(n) g(n)) ● f(n) O(g(n)) = O(f(n) g(n)) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 6 2. Asymptotische Schranken 2.2 Komplexitätsklassen Nicht-effiziente Algorithmen: ➢ exponentielle Komplexität O(2n), O(n!) ● Effiziente Algorithmen ➢ konstante Komplexität O(1) ➢ logarithmische Komplexität O(log n) ➢ lineare Komplexität O(n) ➢ überlinear O(n log n) ➢ polynomiale Komplexität O(n2), O(n3), etc. ● Grundlage: 1 Operation = 1 ns (= 10-9 sec) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 7 3. Wiederholung bekannter Sortieralgorithmen [Wiederholung aus dem 1. Semester] Im Bereich Informatik ist das Sortieren sehr früh ausführlich behandelt worden, denn es ist in fast allen Anwendungsbereichen von Bedeutung (Datenbanken, Compiler, Betriebssysteme, Computer-Grafik, …). Bemerkung: Wir beschränken uns dabei auf die aufsteigende Sortierung von ganzen Zahlen (Schlüssel). Wir betrachten zunächst zwei Sortieralgorithmen aus dem letzten Semester. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 8 3. Wiederholung bekannter Sortieralgorithmen 3.1 Sortieren durch Auswählen Selection Sort wählt das Element mit dem niedrigsten Wert aus und vertauscht es mit dem ersten Element. Von den verbleibenden n-1 Elementen wird dann das kleinste genommen und mit dem zweiten Element vertauscht. Dies wird bis zu den letzten zwei Elementen wiederholt. // Selection Sort mit linearer Suche for (int i=0; i <n-1; i++) { int k=i; int min = A[i]; // Index kleinstes Element // kleinstes Element for (int j=i+1; j<n; j++) { // suche kleinstes Element if (A[j] < min) { k = j; min = A[j]; } } A[k] = A[i]; // vertausche aktuelles Element mit kleinstem Element A[i] = min; } – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 9 3. Wiederholung bekannter Sortieralgorithmen 3.2 Sortieren durch Mischen (Merge Sort) Mischen (engl. merge) bedeutet hierbei das Zusammenfließen von zwei (oder mehreren) geordneten Sequenzen zu einer einzigen sortierten Sequenz. Beispiel: aus (10, 23, 36, 42) und (4, 6, 54, 64) wird (4, 6, 10, 23, 36, 42, 54, 64) Man erhält folgenden Algorithmus: Mergesort(A, Mergesort(A,p,p,r)r) ififpp<<r rthen then qq==(p+r)/2 (p+r)/2 Mergesort(A, Mergesort(A,p,p,q)q) ////Größe Größedes desTeilarrays Teilarraysististgrößer größer11 ////Teile Teileininder derMitte Mitte ////Sortiere Sortierelinken linkenTeil Teil Mergesort(A, Mergesort(A,q+1, q+1,r)r) ////Sortiere Sortiererechten rechtenTeil Teil Merge(A, ////Kombiniere Merge(A,p,p,q,q,r)r) Kombinieredie diebeiden beidenTeile Teile Initialer Aufruf ist: Mergesort(A, 0, n-1); – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 10 4. Heap Sort Heap-Sort ist eine Verbesserung des bereits vorgestellten Selection Sort. Das zeitraubende am Selection Sort ist die Lineare Suche nach dem kleinsten Element im verbleibenden Arrray, was im schlechtesten Fall O(n) Schritte benötigt. // Selection Sort mit linearer Suche for (int i=0; i <n-1; i++) { int k=i; // Index kleinstes Element int min = A[i]; // kleinstes Element for (int j=i+1; j<n; j++) { // suche kleinstes Element if (A[j] < min) { k = j; min = A[j]; } } A[k] = A[i]; // vertausche aktuelles Element mit kleinstem Element A[i] = min; } Die Idee bei Heap-Sort ist es, den noch unsortierten Teil durch eine bessere Datenstruktur (Heap) zu verwalten, so dass es besser (d.h. effizienter) möglich ist, das jeweilige Minimum zu finden. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 11 4. Heap Sort 4.1 Definition Heap Der ADT Baum besteht aus Mengen von Knoten (Elementen) und gerichteten Kanten, die die Knoten verbinden. Dabei gilt: ● Gibt es eine Kante von A nach B, wird A der Vorgänger (Vater) von B und B der Nachfolger von A genannt. Mehrere Nachfolger eines Knotens werden auch Kinder genannt. ● Es gibt immer nur genau einen Knoten (die sog. Wurzel – engl. root), von dem aus alle anderen Knoten zu erreichen sind. Es gibt immer nur genau einen Weg von der Wurzel zu einem Knoten. Bäume und andere Graphen werden wir später noch genauer kennen lernen. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 12 4. Heap Sort Ein Heap ist ein Baum mit folgenden Eigenschaften: ● In den Knoten stehen die zu sortierenden Elemente. Dabei gilt für jeden Knoten außer der Wurzel: Der Wert jedes Knotens ist größer oder gleich dem seiner Nachfolger (Heap-Eigenschaft). ● ● Ein Heap ist ein binärer Baum, d.h. jeder Knoten hat maximal 2 Kinder. Ein Heap ist ein linksvollständiger Baum: d.h. jeder linke Nachfolgerknoten, der einen Bruder mit Kindern hat, muss auch Kinder haben. Der Baum ist also mindestens bis zur vorletzten Ebene vollständig und in der letzten Ebene dürfen nur rechts Knoten fehlen. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 13 4. Heap Sort Ein Heap lässt sich leicht auf ein Array A[1...n] abbilden, indem man die Knoten von links oben nach rechts unten durch nummeriert: ● A[1] = root ● parent(i) = i/2 ● left(i) = 2i ● right(i) = 2i+1 ● Heap-Eigenschaft: A[parent(i)] A[i], i __ __ A[1]_ A[1]_ // \\ A[2] A[3] A[2] A[3] // \\ // \\ A[4] A[4] A[5] A[5] A[6] A[6] A[7] A[7] // \\ A[8] A[8] A[9] A[9] – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 14 4. Heap Sort Damit wird ein Heap zum Sortieren wie folgt benutzt: ● Zuerst wird aus einem ungeordneten Array ein Heap gebaut. (1) ● Somit steht der größte Wert immer in der Wurzel (A[1]), welche nun in den sortierten Teil am Ende des Arrays übertragen wird (4) ● Man beginnt daher ausgehend vom größten Element, d.h. der sortierte Teil steht rechts im Array ● Baue aus den verbleibenden Elementen erneut einen Heap (6) ● Wiederhole diese Vorgehen bis alles sortiert worden ist (3) Heapsort(A) Heapsort(A) // // Sortiere Sortiere Array Array AA (1) (1) Build-Heap(A) Build-Heap(A) (2) (2) HeapSize(A) HeapSize(A) == nn (3) (3) for for i=n i=n downto downto 22 do do (4) swap(A[1], (4) swap(A[1], A[i]) A[i]) (5) HeapSize(A) (5) HeapSize(A) == HeapSize(A)-1 HeapSize(A)-1 (6) Heapify(A,1) (6) Heapify(A,1) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 15 4. Heap Sort 4.2 Erzeugen der Heap-Ordnung: ● Idee: Beginnend beim letzten Knoten, der kein Blatt ist, bis zur Wurzel wird Heapify(i) aufgerufen, welches die Heap-Ordnung herstellt, wenn left(i), right(i) jeweils Heaps sind, ● die Heap-Ordnung selbst in Knoten i aber verletzt ist. ● Einelementige Teilbäume (z.B. die Blätter) erfüllen bereits die HeapEigenschaft. Algorithmus: ● ● BuildHeap(A) BuildHeap(A) for for i= i=n/2 n/2 downto downto 11 do do Heapify(A,i) Heapify(A,i) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 16 4. Heap Sort 4.3 Heapify: Erhalten der Heap-Ordnung Ausgangspunkt: ● Knoten i, bei dem für die Teilbäume mit den Wurzeln left(i) und right(i) die Heap-Eigenschaft erfüllt ist ● Im Knoten i selbst ist die Heap-Eigenschaft verletzt d.h: A[i] < A[left(i)] oder A[i] < A[right(i)] Vorgehen: Heapify(A,i) stellt die Heap-Ordnung wieder her ● Der größte der 3 Knoten i, left(i) und right(i) wird bestimmt und zur Wurzel des Teilbaums gemacht (durch Austausch der Knoten) ● In dem betroffenen Teilbaum ist anschließend ggf. die Heap-Eigenschaft verletzt, was durch einen rekursiven Aufruf von Heapify behoben werden kann. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 17 4. Heap Sort Algorithmus: Heapify Heapify (A,i) (A,i) (1) (1) ll == left(i) left(i) (2) (2) rr == right(i) right(i) (3) (3) if if ll HeapSize(A) HeapSize(A) and and A[l] A[l] >> A[i] A[i] (4) then (4) then max max == ll (5) else (5) else max max == ii (6) (6) if if rr HeapSize(A) HeapSize(A) and and A[r] A[r] >> A[max] A[max] (7) then (7) then max max == rr (8) (8) if if max max ii (9) then (9) then swap(A[i],A[max]) swap(A[i],A[max]) (10) Heapify(A,max) (10) Heapify(A,max) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 18 4. Heap Sort Beispiel: – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 19 4. Heap Sort Beispiel: – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt „Heapsort“ von Gms Eigenes Werk. Lizenziert unter CC BY-SA 3.0 über Wikimedia Commons http://commons.wikimedia. org/wiki/File:Heapsort.svg# /media/File:Heapsort.svg 20 4. Heap Sort Beispiel-Aufgabe: Sortieren der Folge (6, 17, 9, 8, 2, 5, 11, 3) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 21 4. Heap Sort 4.5 Analyse von Heap-Sort ● Heapify(): ● Zeilen 1-9: konstant O(1) ● Entscheidend für die Laufzeit ist die Rekursion. Die maximale Rekursionstiefe ist dabei gleich der Höhe des Heaps. Da ein Heap ein Binärbaum ist, ist diese log2 n. ● Somit ergibt sich ein Aufwand von _____ ● BuildHeap(): ● Heapify() wird O(n)-mal aufgerufen. ● Somit ergibt sich ein Aufwand von _____ ● Heapsort(): ● BuildHeap benötigt O(n log n) Zeit ● Die for-Schleife (2-6) wird O(n)-mal durchlaufen ● Also wird Heapify() O(n)-mal aufgerufen ● Somit ergibt sich ein Aufwand von _____ Aufgabe: Füllen Sie die leeren Felder aus! – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 22 5. Quicksort Quicksort basiert auf der Vertauschmethode (wie Bubblesort) und repräsentiert ein Divide & Conquer-Verfahren (wie Merge Sort), das ein Teilfeld A[p..r] wie folgt sortiert: ● Divide: ● Bestimme ein Pivot-Element A[q] (nach einem noch fest zulegenden Verfahren) ● Das Feld A[p..r] wird zerlegt und umgeordnet in zwei Teilfelder ● A[p..(q-1)] und A[(q+1)..r], so dass gilt: A[i] A[q] A[j] p i q-1 und q+1 j r ● links vom Pivot-Element sind also alle kleiner (gleich) und rechts alle größer (gleich) – sortiert sind die beiden Teile zwar noch nicht, aber das Pivot-Element steht bereits an der richtigen Stelle. ● Conquer: Wende Zerteilungs- und Umordnungsprinzip rekursiv auf beide Teilfelder an. Ein Teil-Array der Größe 1 ist sortiert und braucht/kann nicht mehr zerteilt werden. ● Combine: Ein expliziter Kombinationsschritt ist nicht notwendig, da der Conquer-Schritt bereits sortierte Teilfolgen hinterlässt. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 23 5. Quicksort 5.1 Algorithmus Initialer Aufruf: Quicksort(A, 1, n) Quicksort(A, Quicksort(A, p, p, r) r) (1) (1) if if pp << rr then then (2) (2) (3) (3) (4) (4) qq == Partition(A, Partition(A, p, p, r) r) Quicksort(A, Quicksort(A, p, p, q-1) q-1) Quicksort(A, Quicksort(A, q+1, q+1, r) r) Partition(A, Partition(A, p, p, r) r) //Lumuto-Partition //Lumuto-Partition (1) (1) pivot pivot == A[p] A[p] (2) (2) ll == pp (3) (3) for for i=p+1 i=p+1 to to rr do do (4) (4) if if A[i] A[i] << pivot pivot then then (5) ll == ll ++ 11 (5) (6) swap(A[i], (6) swap(A[i], A[l]) A[l]) (7) (7) swap(A[p], swap(A[p], A[l]) A[l]) (8) (8) return return ll – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 24 5. Quicksort Beispiel-Aufgabe: Sortieren der Folge (6, 17, 9, 8, 2, 5, 11, 3) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 25 5. Quicksort 5.2 Eigenschaften von Quick-Sort ● Es gibt verschiedene Methoden, das Pivot-Element zu bestimmen. Die hier vorgestellte ist recht praktikabel. Eine ausgefeiltere Methode wäre allerdings effizienter. ● Der Worst-Case tritt ein, ● wenn die Folge bereits sortiert ist: ● O(n2) ● Es lässt sich formal beweisen, dass im durchschnittlichen Fall ein Aufwand von O(n log n) entsteht. ● Trotz des schlechteren Worst-Case-Verhaltens ist Quck-Sort im Allgemeinen um einen Faktor 2-3 schneller als Heap-Sort (aufgrund seiner einfacheren Datenstruktur). ● Da bei großen Datenmengen ein Stack-Überlauf eintreten kann, wird in der Praxis häufig eine iterative Variante bevorzugt. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 26 6. Analyse rekursiver Algorithmen Rekursive Algorithmen sind meist nur schwer im Aufwand abzuschätzen. Mit herkömmlichen Methoden kann dies auf zwei Arten geschehen: 1. Der Rekursionsbaum wird bildlich dargestellt und dessen Höhe und Breite ermittelt. Dabei ist es i.A. schwierig, alle Aufwände korrekt zu erfassen. 2. Eine Rekurrenzgleichung der unten stehenden Form wird durch einen formalen Beweis gelöst, was sehr mühsam ist (Iterationsmethode). Bei der im Folgenden vorgestellten Methode – dem Master Theorem – wird dagegen auf die Anwendung von drei Grundregeln vertraut, die (mit etwas Übung) in wenigen Schritten eine Abschätzung ermöglichen. Voraussetzung dabei ist, dass sich der Aufwand eines rekursiven Algorithmus oft (aber nicht immer) durch folgende Gleichung darstellen lässt (D&C): T n=aT n /b f n Zerlegung in a Teilprobleme der Größe n/b, jeweils mit Aufwand T(n/b) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt Kosten für Zerteilung und Kombination (je Knoten im Rekursionsbaum) 27 6. Analyse rekursiver Algorithmen 6.1 Iterationsmethode [Wiederholung aus dem 1. Semester] Für Mergesort erhält man folgende (vereinfachte) Rekurrenz-Gleichung: T(n) = 2 T(n/2) + O(n) = 2 T(n/2) + n Mit der Iterationsmethode erhält man folgende Abschätzung: T n=2 T n/ 2 n =2 T 2 T n / 4 n / 2 n =4T n /42n =4 T 2 T n /8n/ 42n =8 T n/ 83n =... =n T 1n log 2 n =n log 2 nn =O n log n – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 28 6. Analyse rekursiver Algorithmen Beispiele zur Herleitung der Rekurrenz-Gleichung: int seltsam (int n) { if (n <= 2) return 2; else return seltsam(n/2)*2; } Es gilt: T(n) = 1 T(n/2) + 1, d.h. a=1, b=2 und f(n)=1 unsigned long fac (unsigned long n) { if (n == 1) return 1; else return fact(n-1)*n; } Es gilt: T(n) = 1 T(n-1) + 1, d.h. keine Werte für a, b und f(n) – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 29 6. Analyse rekursiver Algorithmen 6.2 Master-Theorem (Master Method) – vereinfachte Form: c T n =aT n / b O n (1) Falls gilt logb a > c : dann ist: T(n) = O(nlogb a) (2) Falls gilt logb a = c : dann ist: T(n) = O(nc log n) (3) Falls gilt logb a < c : dann ist: T(n) = O(nc) Gilt a=b, ist logb a = 1. Gilt keiner der drei Fälle oder lassen sich keine Werte für a, b oder f(n) finden, ist das Master-Theorem nicht anwendbar. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 30 6. Analyse rekursiver Algorithmen 6.2 Master-Theorem (Master Method) – vereinfachte Form: c T n =aT n / b O n (1) Falls gilt a / bc > 1 : dann ist: T(n) = O(nlogb a) (2) Falls gilt a / bc = 1 : dann ist: T(n) = O(nc log n) (3) Falls gilt a / bc < 1 : dann ist: T(n) = O(nc) Gilt a=b, ist logb a = 1. Gilt keiner der drei Fälle oder lassen sich keine Werte für a, b oder f(n) finden, ist das Master-Theorem nicht anwendbar. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 31 6. Analyse rekursiver Algorithmen Beispiele/Aufgaben zur Anwendung des Master-Theorems: ● ● Durch Master-Theorem lösbar: ➢ T(n) = T(n/2) + 1 ➢ T(n) = 2 T(n/2) + n ➢ T(n) = 2 T(n/2) + n2 Nicht durch Master-Theorem lösbar: ➢ T(n) = T(n/2) + log n – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 32 6. Analyse rekursiver Algorithmen Aufgabe: Analysieren Sie die beiden folgenden Algorithmen! bool search(struct tree *p, int info) { if (p==NULL) return false; if (info == p->info) return true; else if (info < p->info) return search(p->left, info); else return search(p->right, info); } Mergesort(A, Mergesort(A,p,p,r)r) ififpp<<r rthen then qq==(p+r)/2 (p+r)/2 Mergesort(A, Mergesort(A,p,p,q)q) // search info in tree p ////Größe Größedes desTeilarrays Teilarraysististgrößer größer11 ////Teile Teileininder derMitte Mitte ////Sortiere Sortierelinken linkenTeil Teil Mergesort(A, Mergesort(A,q+1, q+1,r)r) ////Sortiere Sortiererechten rechtenTeil Teil Merge(A, ////Kombiniere Merge(A,p,p,q,q,r)r) Kombinieredie diebeiden beidenTeile Teile – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 33 7. Einführung Suchen ● ● ● ● Suchen in der Informatik heißt, ein Element (mit einem bestimmten Schlüssel) in einer Menge von gespeicherten Werten zu finden. Dies ist ein genauso elementares Problem wie das Sortieren. Problemstellung: Wie verwaltet (Einfügen, Finden, Löschen) man effizient (Zeit & Platz) große und sich ständig ändernde Datenstrukturen? Beispiel: Der Compiler für einer Programmiersprache verwaltet eine Symboltabelle ● Neue Identifier (z.B. Variablen) werden darin gespeichert ● Abfrage auf doppelte Definitionen ● Auswertung von Ausdrücken Beispiel zum Beispiel: int x; char y; int x; ... x = z + y; – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 34 7. Einführung Suchen Genaue Problemstellung • Zur Verarbeitung durch einen Algorithmus (bzw. Computerprogramm) wird eine Datenstruktur zur Verwaltung der Daten gebraucht • Definition: A Dictionary is a dynamic set that supports the following operations: • INSERT an element: D x T -> D • DELETE an element: D x T -> D • MEMBER/SEARCH (is it in the set ?): D x T -> T or bool • Dabei sind • D: Ausprägung der Datenstruktur Dictionary • T: Wertebereich des verwalteten Schlüsselelemente • Im Folgenden nur Betrachtung von nicht-negativen ganzen Zahlen • Ziel: Effiziente Implementierung der o.g. Operationen – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 35 7. Einführung Suchen 7.1 Dictionary als unsortiertes Array Legt man die Daten in unsortiert Weise in einem Array (oder Vector) ab, so können die Dictionary-Funktionen wie folgt implementiert werden: ● INSERT: einfach am Ende ● SEARCH: durch eine sequentielle Suche [Wiederholung aus dem 1. Semester] int key, pos; bool found = false; cout << "Suche nach: "; cin >> key; for (int i=0; i<n; i++) if (key == A[i]) { found = true; pos = i; break; } if (!found) cout << "Element nicht vorhanden!!" << endl; else cout << "Element liegt an Stelle " << pos << endl; – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 36 7. Einführung Suchen Aufgabe: Wie sieht damit die DELETE-Operation aus? Aufgabe: Laufzeit-Analyse Be s t W o rs t Av e ra g e IN S ERT S EARCH D ELETE – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 37 7. Einführung Suchen 7.2 Dictionary als sortiertes Array Sorgt man dafür, dass das Array immer sortiert ist, ist die Suche effizienter: ● SEARCH: durch eine binäre Suche: [Wiederholung aus dem 1. Semester] ➢ Zunächst wird das mittlere Element untersucht: ist es der Suchschlüssel ist man fertig. ➢ Ist der Suchschlüssel kleiner als das mittlere Element, wird der linken Hälfte des Arrays mittels des gleichen Verfahrens bearbeitet, ansonsten der rechte Teil. 1 2 3 4 5 15 Beispiel: Suche „4“ Ausgangssituation ● Suche rechts 1 2 3 4 5 15 Suche links 1 2 3 4 5 15 INSERT: ➢ Finden der Einfügestelle durch eine binäre Suche ➢ mit anschließendem Verschieben – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 38 7. Einführung Suchen Beispiel: Finden der Einfügestelle durch eine binäre Suche: INSERT(35) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 9 11 23 32 33 34 40 42 43 49 50 55 66 67 99 16 35 muss an Position 7 Anschließend: verschieben ab Position 8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 9 11 23 32 33 34 35 40 42 43 49 50 55 66 67 99 – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 39 7. Einführung Suchen Aufgabe: Laufzeit-Analyse Be s t W o rs t Av e ra g e IN S ERT S EARCH D ELETE – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 40 7. Einführung Suchen Dictionary mit Direkter Adressierung • Man reserviere für jedes potenzielle Element einen Eintrag: 1 Universum der Schlüssel 1 2 5 6 4 7 9 3 2 3 5 4 8 Dictionary-Operationen • Direct_Address_Search(T, k) return T[k] • Direct_Address_Insert(T, x) T[key(x)] = x • Direct_Address_Delete(T, x) T[key(x)] = NIL – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 6 7 8 9 NIL 2 T 3 4 NIL NIL NIL 8 NIL Laufzeit Jeweils O(1) Platz O(|Wertebereich|) 41 8. Hashing 8.1 Grundlegendes Hash-Verfahren • Ambition: Vereinigung der Vorteile von • Direkter Adressierung: Zugriff in Zeit O(1) • (Un)Sortiertem Feld: Platzbedarf O(n) • Hashverfahren berechnet die Adresse in einem Array T[0..(m-1)] (Hashtabelle), an der ein Schlüssel gespeichert werden soll (m > n) • Die Berechnung erfolgt durch eine Hashfunktion h, die jedem Schlüssel seine Hashadresse zuordnet. • Die Hashfunktion ist eine Abbildung h: U {0,...,m-1} von der Menge U (Universum) der möglichen Schlüsselwerte in die Indizes der Hashtabelle – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 42 8. Hashing • Ziel: möglichst gleichmäßige Streuung der Schlüssel T 0 k9 k1 k8 X k7 h(k4) k3 k2 X h(k2) = h(k5) k4 X k5 h(k1) m-1 • h(k3) 1 k6 k10 X X Aber: manche Elemente erhalten denselben Hashwert, d.h. es gibt k k‘ mit h(k) = h(k‘) Kollision – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 43 8. Hashing Beispiel eines Hashing-Verfahrens • Hashfunktion • Divisionsmethode: h(k) = k mod m • m = 13 (Größe der Hashtabelle) • Nacheinander Einfügen der Schlüssel k = 12, 1, 54, 40 12 mod 13 = 12 1 mod 13 = 1 54 mod 13 = 2 40 mod 13 = 1 Kollision – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 0 1 2 3 4 5 6 7 8 9 10 11 12 1 54 12 44 8. Hashing 8.2 Kollisionswahrscheinlichkeit • Beispiel: • Man benutze für Benutzerdaten eine Hashfunktion auf Basis des Geburtstags (Tag, Monat) • Also gilt: m = 365 ● Wahrscheinlichkeit für eine Kollision: n P Kollision =1−∏ Pi i=1 n 1 m−i1 n ∏ m i =1 m m−1m−2⋯m−n1 =1− n m =1− n PKollision 10 22 23 50 100 0,1169 0,4756 0,5072 0,9703 0,9999 • Birthday Paradoxon: • Treffen 23 Personen in einem Raum zusammen, so ist die Wahrscheinlichkeit, dass zwei von ihnen am gleichen Tag Geburtstag haben bereits größer als 1/2. • Kollisionen sind also unvermeidbar! – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 45 8. Hashing 8.3 Hashing mit Zeichenketten Wenn Zeichenketten als Schlüssel dienen sollen, so können sie einfach in eine ganze Zahl kodiert und in die Hashfunktion eingebracht werden. Algorithmus: hash // hash (m, (m, k) k) // m: m: Modulus, Modulus, k: k: Zeichenkette Zeichenkette hh == 00 for for i=1 i=1 to to length[k] length[k] do do hh == (128 (128 ** hh ++ num(k[i])) num(k[i])) %% mm // // num(c) num(c) liefert liefert die die Ordnungszahl Ordnungszahl zu zu cc return return hh – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 46 8. Hashing 8.4 Kollisionsvermeidung • Praktische Tipps (für Divisionsmethode: h(k) = k mod m) ➢ Sei m eine gerade Zahl, dann gilt: Ist k gerade so ist auch h(k) gerade, und umgekehrt ➢ Sei m eine Zweierpotenz d.h. m = 2i, dann gilt: Es werden nur die niederwertigen i Bits verwendet ➢ Sei m = 2i-1, dann gilt: Bei Umwandlung aus Zeichenketten erhalten alle Permutationen eines Begriffs denselben Wert, z.B. h(ABCD) = h(DACB) ➢ Man wähle m ≥ 1,1n • Folgerung: • Man wähle m als Primzahl nicht in der Nähe einer Zweierpotenz • Beispiel: n = 500, m = 551 – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 47 8. Hashing 8.5 Kollisionsbehandlung Was tun im Falle einer Kollision? 1. Möglichkeit: Verkettung (Chaining): Jedes Tabellenfeld kann beliebig viele Elemente aufnehmen (d.h. Listenbildung) T 0 k6 k10 k9 1 k1 k3 k4 k2 k5 k7 k3 k2 k4 k5 k1 m-1 – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 48 8. Hashing Pseudo-Code void insert (x) // insert x in Hash Table i = h(x); // derive ha sh value if (H[i] == NULL) // entry not yet used H[i] = ListCreate(x); else H[i] = ListAppend(H[i], x); bool isMember(x) // look for x in Ha sh Table list = H[h(x)]; // determ ine linked list if (ListFound(list, x)) // found x return true; else // not found return false; } – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 49 8. Hashing Beispiel: const int M=3; ... struct element *H[M]; // ha sh ta ble int h(int x) { // ha sh fu nction return x%M; } Aufgabe: Zeichnen Sie (schrittweise) die Hash-Tabelle bei Eingabe von: 10, 3, 1, 4, 21, 7, 11 – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 50 8. Hashing Kollisionsbehandlung • Was tun im Falle einer Kollision? • 2. Offene Adressierung: Jedes Tabellenfeld kann nur eine feste Anzahl b von Elementen aufnehmen (i.A. b=1) 0 • D.h. es wird nach einer neuen Lücke gesucht 1 1 • Beispiel von eben 12 mod 13 = 12 1 mod 13 = 1 54 mod 13 = 2 40 mod 13 = 1 • Analoger Ablauf für MEMBER & DELETE – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 2 3 4 5 6 7 8 9 10 11 12 54 40 12 51 8. Hashing • Formell/praktisch erweitert man dabei die Hashfunktion zu einer Sondierungsfolge h: U x {0,...,m-1} {0,...,m-1} • z.B.: Lineares Sondieren (Probing): h(x, i) := (h‘(x) + i) mod m Hash-Insert(T, Hash-Insert(T, k) k) ii == 00 repeat repeat jj == h(k,i) h(k,i) if if T[j] T[j] == NIL NIL then T[j] then T[j] == kk return return jj else else ii == i+1 i+1 until until i=m i=m error „hash error „hash overflow“ overflow“ – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt Hash-Search(T, Hash-Search(T, k) k) ii == 00 repeat repeat jj == h(k,i) h(k,i) if if T[j] T[j] == kk then then return return jj ii == i+1 i+1 until until T[j]=NIL T[j]=NIL or or i=m i=m return NIL return NIL 52 8. Hashing 8.6 Aufwand für Hashing • Im Best-Case • Keine Kollision O(1) • Im Worst-Case • Alle Elemente werden durchlaufen O(n) • Im Average-Case • Es lässt sich formal beweisen, dass gilt: O(1) • Achtung: im Falle einer schlechten Hash-Funktion, tritt der Worst-Case häufig ein!! – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 53 8. Hashing Experimentelle Resultate Laufzeitverhalten: Sortiertes Array vs. Hashing 60 50 Zeit in s 40 Sortiertes Array Hashing 30 20 10 0 0 100000 200000 300000 400000 500000 600000 700000 800000 900000 1000000 Größe n – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 54 8. Hashing 8.7 Erweiterungs- und Veränderungsmöglichkeiten • Andersartige Hash-Funktionen • Multiplikationsmethode: man multipliziere mit einer Konstanten und extrahiere Nachkommastellen • Universelles Hashing: Kombination mehrerer Hash-Funktionen • Verschiedenartige Offene Adressierung: ( im Vergleich zum Linearen Sondieren: h(x, i) := (h‘(x) + i) mod m ) • Quadratisches Sondieren: h(x, i) := (h‘(x) + c1i + c2i2) mod m • Double Hashing: h(x, i) := (h1(x) + i*h2(x)) mod m – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 55 8. Hashing Andersartige Lösungen • Dictionaries lassen sich auch durch sogenannte Balancierte Suchbäume implementieren • Vorteil: Für den Worst-Case kann eine Laufzeit von O(log n) garantiert werden • Nachteil: kompliziert und viele technischen Details, d.h. schwierig zu implementieren • Daher wird in der Praxis Hashing oft bevorzugt ________________________________________________________________ Weiterführende Themen: ● Bäume und Graphen – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 56 9. Assoziative STL-Container Die Templates [multi]set und [unordered_][multi]map werden assoziative Container genannt, da sie einen Wert mit einem Schlüssel assoziieren d.h. verknüpfen. Sie werden im Allgemeinen benutzt, um die Suche nach Daten zu unterstützen. Achtung: Assoziative Container haben einen schwer zu kalkulierenden Aufwand Und können sehr speicherintensiv sein! 9.1 set Eine Menge (set) kann ein Element nur einmal enthalten. Sollen mehrere Elemente in einer Menge enthalten sein, die den selben Wert repräsentieren, so kann ein [multi]set verwendet werden. Das folgende Beispiel zeigt beide Möglichkeiten. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 57 9. Assoziative STL-Container #include <iostream> #include <set> using namespace std; template<typename container> void test(container & T) { // einfügen T.insert(1); T.insert(42); T.insert(2); T.insert(1); // ausgeben for (typename container::iterator it = T.begin(); it != T.end(); ++it) { cout << *it << endl; } cout << endl; return; } 1 2 int main() { 42 set <unsigned int> s; test(s); 1 multiset <unsigned int> ms; 1 test(ms); 2 return 0; 42 } – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 58 9. Assoziative STL-Container 9.2 map In einer map werden Schlüssel-Werte-Paare abgelegt: ● ● ● Ein solches Paar wird mit der Operation make_pair(key, element) gebildet Mit insert() wird das Paar in der map abgelegt. Mit find(key) kann danach in der map gesucht werden. Eine multimap erlaubt mehrere Einträge zu einem Schlüssel (d.h. ADT Dictionary). Das folgende Beispiel zeigt beide Möglichkeiten. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 59 9. Assoziative STL-Container #include <stdlib.h> #include <iostream> #include <map> using namespace std; /* srand, rand */ template<typename Map> void test(Map & M); int main() { map<unsigned int, string> m; test(m); multimap<unsigned int, string> mm; test(mm); } return 0; – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 60 9. Assoziative STL-Container template<typename Map> void test(Map & M) { unsigned int tmp_knr = rand(); // make_pair() verknüpft den Schlüssel mit den Daten (Kundennr mit Namen) M.insert(make_pair(tmp_knr, "Altenbernd, Peter")); tmp_knr = rand(); / insert() fügt ein neu verknüpftes Paar ein M.insert(make_pair(tmp_knr, "Schütte, Alois")); / tmp_knr = rand(); M.insert(make_pair(tmp_knr, "Lopez, Jennifer")); M.insert(make_pair(tmp_knr, "Lopez, Jennifer")); unsigned int knr3 = tmp_knr; typename Map::iterator found_it = M.find(knr3); // suche über Kundennummer if(found_it != M.end()) cout << (*found_it).second << " wieder gefunden!" << endl; // Ausgabe aller Elemente for(typename Map::iterator it = M.begin(); it != M.end(); ++it) cout << (*it).first << ": " << (*it).second << endl; // first = Schlüssel, second = verknüpfte Daten cout << endl; } return; – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 61 9. Assoziative STL-Container Die Ausgabe erfolgt sortiert (über das Schlüssel-Element): Lopez, Jennifer wieder gefunden! 846930886: Schütte, Alois 1681692777: Lopez, Jennifer 1804289383: Altenbernd, Peter Lopez, Jennifer wieder gefunden! 424238335: Lopez, Jennifer 424238335: Lopez, Jennifer 1714636915: Altenbernd, Peter 1957747793: Schütte, Alois – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 62 9. Assoziative STL-Container 9.3 unordered_map Eine wirkliche Hash-Tabelle wird am ehesten durch eine unordered_map repräsentiert. Im Gegensatz zu einer normalen map ist die Ablage also unsortiert und der Zugriff erfolgt über eine (interne) Hash-Funktion. Bemerkung: Eine unordered_map gibt es erst seit dem C++-11-Standard. Eine unordered_multimap erlaubt mehrere Einträge zu einem Schlüssel. Beide Möglichkeiten ergänzen das Beispiel von eben. – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 63 9. Assoziative STL-Container … #include <unordered_map> … int main() { unordered_map<unsigned int, string> um; // nur C++-11 test(um); // ggf. mit eigener Hash-Funktion - nur C++-11 unordered_multimap<unsigned int, string> umm; test(umm); } return 0; Lopez, Jennifer wieder gefunden! 719885386: Altenbernd, Peter 596516649: Lopez, Jennifer 1649760492: Schütte, Alois Lopez, Jennifer wieder gefunden! 1189641421: Altenbernd, Peter 1350490027: Lopez, Jennifer 1350490027: Lopez, Jennifer 1025202362: Schütte, Alois – 5. Einführung Sortieren und – PG Suchen II – SoSe – PAD 2009 II ––SoSe Hochschule 2015 –Darmstadt Hochschule Darmstadt 64