Informatik 2 Informatik 2 Dipl.-Inf. Domenic Jenz ([email protected]) http://informatikdhs.npage.de/ Ab 29. April 2014 Informatik 2 Einleitung Termine 1. 29.4.2014 2. 6.5.2014 3. 13.5.2014 4. 20.5.2014 5. 27.5.2013 6. 3.6.2013 (Nachschreibeklausur !) 7. 10.6.2013 8. 17.6.2013 9. 24.6.2013 10. 1.7.2013 (Klausur !) Informatik 2 Aufwandsabschätzung Landau-Symbole Landau-Symbole I Mit fleißigem Zählen kann man oft die Anzahl an Operationen als Funktion f (n) in Abhängigkeit von der Größe der Eingabedaten aufstellen. I Häufig interessiert aber nicht die genaue Anzahl an Operationen, sondern das asymptotische Laufzeitverhalten. I Dieses wird formal in den Landau-Symbolen erfasst. I Definition von ein paar der Interessantesten: f ∈ O(g) ⇔ ∃ c > 0 ∃ x0 > 0 ∀x > x0 : |f (x)| ≤ c · |g(x)| f ∈ Ω(g) ⇔ g ∈ O(f ) f ∈ Θ(g) ⇔ f ∈ O(g) ∧ g ∈ O(f ) Informatik 2 Aufwandsabschätzung Landau-Symbole Häh ?!!! Die Definitionen etwas anders: I f ∈ O(g): f wächst langsamer oder ungefähr gleich schnell wie g. Bei Polynomen: Grad von f ist höchstens so groß wie Grad von g. f (n) limn→∞ g(n) existiert. I f ∈ Ω(g): f wächst schneller oder ungefähr gleich schnell wie g Bei Polynomen: Grad von f ist mindestens so groß wie Grad von g. I f ∈ Θ(g): f wächst ungefähr genau so schnell wie g Bei Polynomen: der Grad von f ist gleich dem Grad von g. f (n) limn→∞ g(n) = c, c 6= 0 Informatik 2 Aufwandsabschätzung Landau-Symbole Beispiele f(n) 7 log(n) + 42 2n n log(4n) + n 1 2 2 n + log n n3 − 4n2 2n 5n 7sin2 (n) √ n n O(n) + + + - O(n log n) + + + + - O(n2 ) + + + + + + O(en ) + + + + + + + + Ω(n2 ) + + + + - Θ(n2 ) + - Informatik 2 Aufwandsabschätzung Landau-Symbole Was bedeutet also nun eine Verdoppelung der Problemgröße für die Laufzeit der Programme ? f (n) ∈ O(log(n)) : f(n) = 100 → f (2n) ≈ 101 → f (8n) ≈ 108 f (n) ∈ O(n) : f(n) = 100 → f (2n) ≈ 200 → f (8n) ≈ 800 f (n) ∈ O(n2 ) : f(n) = 100 → f (2n) ≈ 400 → f (8n) ≈ 6400 f (n) ∈ O(n3 ) : f(n) = 100 → f (2n) ≈ 800 → f (8n) ≈ 51200 f (n) ∈ O(2n ) : f(n) = 100 → f (2n) ≈ 10000 → f (8n) ≈ 1016 Bitte beachten: Die Größe der Probleme, die hier in 100 Zeiteinheiten berechnet werden kann ist natürlich unterschiedlich und wird mit jeder Zeile kleiner ! Informatik 2 Aufwandsabschätzung Beispielprobleme Code 1 1 2 3 4 5 6 7 8 9 10 11 12 int getMax (int field[], int n) { int tempMax = field[0]; for (int i = 1; i < n; i++) { if (tempMax < field[i]) { tempMax = field[i]; } } return tempMax; } Laufzeitkomplexität ist in O(n) Informatik 2 Aufwandsabschätzung Beispielprobleme Code 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int getMaxProd (int field[], int n) { int tempMax = field[0]*field[1]; for (int i = 0; i < n; i++) { for (int j = i+1; j < n; j++) { if (tempMax < field[i]*field[j]) { tempMax = field[i] * field[j]; } } } return tempMax; } Laufzeitkomplexität ist in O(n2 ) Informatik 2 Aufwandsabschätzung Beispielprobleme Code 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void differentiate2 (int nodes[],int result[],int size) { int weights[3] = {1,-2,1}; result[0] = weights[1] * nodes[0] + weights[2] * nodes[1]; result[size-1] = weights[0] * nodes[size-2] + weights[1] * nodes[size-1]; for (int i = 1; i < size-1; i++){ result[i] = 0; for (int j = -1; j <= 1; j++){ result[i] += weights[j+1] * nodes[i+j]; } } } Laufzeitkomplexität ist in O(n) Informatik 2 Aufwandsabschätzung Beispielprobleme Code 4 1 2 3 4 5 6 7 8 9 10 11 int addRek (int values[], int start, int size) { if (size == 1){ return values[start]; } else{ int size1 = size / 2; int size2 = size - size1; return addRek (values, start, size1) + addRek (values, start + size1, size2); } } Laufzeitkomplexität ist O(n) Anfangen mit f (n) = f n2 + f n2 + 1 mit f (1) = 0, dann die Vermutung f (n) = n − 1 per Induktion beweisen. Informatik 2 Aufwandsabschätzung Mastertheorem Mastertheorem (Aus Uwe Schöning: Algorithmik) Gegeben sei eine Rekursiongleichung der Form T (n) = m X T (αi n) + Θ(nk ), 0 < αi < 1, m ≥ 1, k ≥ 0 i=1 Dann kann T (n) asymptotisch wie folgt abgeschätzt werden: Pm k , αi < 1 Θ(nk ) Pi=1 m k T (n) ∈ Θ(nk log n) , i=1 αi = 1 P m k Θ(nc ) , i=1 αi > 1 dabei ist c die Lösung zu Pm c i=1 αi = 1. Informatik 2 Aufwandsabschätzung Mastertheorem Mastertheorem speziell Häufig sind die αi gleich (d.h. das Feld wird gleichmäßig zerteilt): T (n) = m · T (αn) + Θ(nk ), 0 < α < 1, m ≥ 1, k ≥ 0 Jetzt kann T (n) asymptotisch wie folgt abgeschätzt werden: , m · αk < 1 Θ(nk ) k T (n) ∈ Θ(n log n) , m · αk = 1 Θ(nc ) , m · αk > 1 m In dem Fall vereinfacht sich der letzte Teil: c = − ln ln α Informatik 2 Aufwandsabschätzung Mastertheorem Beispiele I T (n) = 2 · f ( n2 ) + 1 ⇒ T (n) ∈ Θ(n1 ), mit ln 2 m = 2, α = 12 , k = 0 und somit 3.Fall mit c = − ln (1/2) =1 I T (n) = 8 · f ( n3 ) + n2 ⇒ T (n) ∈ Θ(n2 ), mit m = 8, α = 13 , k = 2 und somit 1.Fall I T (n) = 9 · f ( n3 ) + n2 ⇒ T (n) ∈ Θ(n2 log n), mit m = 9, α = 13 , k = 2 und somit 2.Fall Informatik 2 Einfache Sortieralgorithmen Allgemeines I Die Aufgabe von Sortieralgorithmen ist es eine Folge a0 , a1 , . . . , an−1 von Elementen anhand einer Ordnungsrelation zu sortieren. I Für die Implementierungen hier soll die Ordnungsrelation ≤ auf den ganzen Zahlen verwendet werden. (d.h. die Zahlen sollen aufsteigend sortiert werden). I Also ist die Permutation π gesucht mit aπ(i) ≤ aπ(i+1) für i ∈ {0, . . . , n − 2} I Für die Aufwandsabschätzungen sollen die Vergleiche von Folgenelementen abgeschätzt werden. I Als Maß für die Unsortiertheit eignet sich die Inversionszahl (oder Fehlstand): I(A) = |{(i, j) | i < j und ai > aj }| . 0 (sortiert) ≤ I(A) ≤ n(n−1) 2 (falsch herum sortiert) Informatik 2 Einfache Sortieralgorithmen Ein ganz einfacher Sortieralgorithmus: Bogosort Gegeben sei eine Folge von Zahlen. 1. Überprüfe ob die Folge sortiert ist. 2. Wenn ja, dann höre auf und vermelde Erfolg 3. Wenn nein, mische die Folge zufällig durch und gehe zu Schritt 1 Aufwandsabschätzungen für diesen Algorithmus: I Best case: O(n) I Average case: O(n · n!) I Worst case: Algorithmus bricht nicht ab Da wird es wohl bessere Algorithmen geben... Informatik 2 Einfache Sortieralgorithmen Selection Sort Selection Sort I Das Feld sei in einen sortierten (anfangs leer) und unsortierten Bereich aufgeteilt. Am Anfang gehört alles zum unsortierten Bereich. I Finde das Minimum aus dem unsortierten Bereich I Vertausche es mit dem ersten Element des unsortierten Bereichs Informatik 2 Einfache Sortieralgorithmen Selection Sort for i ← 0 . . . n − 1 do minIndex ← Minimum von ai , . . . , an−1 if i 6= minIndex then Vertausche aminIndex mit ai end if end for Informatik 2 Einfache Sortieralgorithmen Selection Sort 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 typedef int Datatype; void swap (Datatype* a, Datatype* b){ Datatype helper = *a; *a = *b; *b = helper; } void selectionSort (Datatype field[], int fieldSize){ int minIndex; for (int i = 0; i < fieldSize - 1; i++){ minIndex = i; for (int j = i+1; j < fieldSize; j++){ if (field[j] < field[minIndex]){ minIndex = j; } } if (i != minIndex){ swap (&(field[i]), &(field[minIndex])); }}} Informatik 2 Einfache Sortieralgorithmen Selection Sort Aufwandsabschätzung Selection Sort I I I Best Case: O(n2 ) Die Folge ist schon sortiert: P n·(n−1) Vergleiche: n−1 ≈ 12 n2 , i=1 i = 2 Tauschaktionen: 0 Worst Case: O(n2 ) Die Folge ist verkehrtrum sortiert: ≈ 21 n2 , Vergleiche: n·(n−1) 2 Tauschaktionen: n − 1 ≈ n Average Case: O(n2 ) Vergleiche: n·(n−1) ≈ 21 n2 , 2 Tauschaktionen: ≤ n − 1 ≈ n Informatik 2 Einfache Sortieralgorithmen Bubble Sort Bubble Sort I Es werden zwei benachbarte Elemente a[i] und a[i+1] verglichen. Falls a[i] > a[i+1] ist, werden die beiden vertauscht. I Danach wird das nächste Paar miteinander verglichen. I Ist man am Ende angelangt, beginnt man wieder am Anfang. I Das macht man solange bis es in einem Durchlauf keine Vertauschungen mehr gibt. I Die großen Elemente “blubbern” dabei nach oben Figure : Visualisierung von Bubblesort. Aus engl. Wikipedia Informatik 2 Einfache Sortieralgorithmen Bubble Sort Vertauschungen ← 1 max ← n − 1 tempMax ← 0 while Vertauschungen > 0 do Vertauschungen ← 0 for i ← 0, . . . , max − 1 do if ai > ai+1 then Vertausche ai mit ai+1 Vertauschungen ← Vertauschungen + 1 tempMax ← i end if end for max ←tempMax end while Informatik 2 Einfache Sortieralgorithmen Bubble Sort 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void bubbleSort (Datatype field[], int size){ int swaps = 1; int max = size-1; int tempMax = 0; while (swaps > 0){ swaps = 0; for (int i = 0; i < max; i++){ if (field[i] > field[i+1]){ swap (&(field[i]), &(field[i+1])); swaps++; tempMax = i; } } max = tempMax; } } Informatik 2 Einfache Sortieralgorithmen Bubble Sort Aufwandsabschätzung Bubblesort Beim Average Case wird eine Gleichverteilung der Elemente angenommen (jede Permutation ist gleich wahrscheinlich). I Best Case: O(n) Die Folge ist schon sortiert: Vergleiche: n-1, Tauschaktionen: 0 I Worst Case: O(n2 ) Die Folge ist verkehrtrum sortiert: Pn−1 Vergleiche: i=1 i = n·(n−1) ≈ 12 n2 , 2 Tauschaktionen: n·(n−1) ≈ 12 n2 2 I Average Case: O(n2 ) Vergleiche: ≈ 21 n2 , Tauschaktionen: ≈ 14 n2 Leider auch bei kleinem Fehlstand quadratischer Aufwand (z.B. 2 3 4 5 6 1). Informatik 2 Einfache Sortieralgorithmen Insertion Sort Insertion Sort I Das Feld sei in einen sortierten und unsortierten Teil aufgeteilt. I Am Anfang ist natürlich nur das erste Element im sortierten Teil und der ganze Rest bildet den unsortierten Teil I In jedem Schritt wird das erste Element aus dem unsortierten Teil genommen und an die richtige Stelle im sortierten Teil eingefügt. Dazu wird es mit dem linken Nachbarn verglichen und gg.falls vertauscht bis der linke Nachbar kleiner ist. Informatik 2 Einfache Sortieralgorithmen Insertion Sort Pseudocode 1 for i ← 1 . . . n − 1 do pos ← i − 1 while apos > apos+1 und pos ≥ 1 do Vertausche apos mit apos+1 pos ← pos − 1 end while end for Informatik 2 Einfache Sortieralgorithmen Insertion Sort Pseudocode 2 for i ← 1 . . . n − 1 do wert ← ai pos ← i − 1 while apos > wert und pos ≥ 1 do apos+1 ← apos pos ← pos − 1 end while apos+1 ← wert end for Informatik 2 Einfache Sortieralgorithmen Insertion Sort 1 2 3 4 5 6 7 8 9 10 11 12 13 void insertionSort (Datatype field[], int fieldSize){ int value; int testPos; for (int i = 1; i < fieldSize; i++){ value = field[i]; testPos = i - 1; while ((testPos >= 0) && (field[testPos] > value)) { field[testPos + 1] = field[testPos]; testPos--; } field[testPos + 1] = value; } } Informatik 2 Einfache Sortieralgorithmen Insertion Sort Aufwandsabschätzung Insertion Sort I I I Best Case: O(n) Die Folge ist schon sortiert: Vergleiche: n-1, Tauschaktionen: 0 Worst Case: O(n2 ) Die Folge ist verkehrtrum sortiert: P n·(n−1) ≈ 12 n2 , Vergleiche: n−1 i=1 i = 2 P n2 +n−2 Tauschaktionen: n−1 ≈ 12 n2 i=1 i + n − 1 = 2 Average Case: O(n2 ) Vergleiche: ≈ 14 n2 , Tauschaktionen: ≈ 41 n2 Aber: Bei schon fast sortierten Folgen (geringer Fehlstand) ist das Verfahren recht flott (in jedem Schritt wird der Fehlstand um 1 verringert, also O(n + I(A)). Informatik 2 Effiziente Sortieralgorithmen Divide et Impera! I Oder auch Divide & Conquer I Ursprünglich Militärstrategie (schon Sunzi sprach davon 500 v.Chr.) I Zerteile ein Problem in kleinere und löse dann diese I Sehr schön mit Rekursion zu machen : In einer Funktion wird das Problem zerlegt und die gleiche Funktion mit den kleineren Teilen wieder aufgerufen. Danach muß evtl. noch zusammengefügt werden. I Quicksort und Mergesort bedienen sich dieser Strategie. I Natürlich auch viele andere Algorithmen Informatik 2 Effiziente Sortieralgorithmen Quicksort Quicksort Übersicht 1. Suche ein Element aus der Folge aus (Pivotelement). 2. Ordne die Liste so um , daß alle Elemente, die kleiner als das Pivotelement sind, links von diesem und alle Elemente, die größer sind, rechts von diesem stehen. 3. Sortiere nun getrennt die Elemente links vom Pivotelement und die Elemente rechts davon (natürlich mit Quicksort) 4. Eine Folge mit 0 oder 1 Element ist von sich aus sortiert. Informatik 2 Effiziente Sortieralgorithmen Quicksort Partitionieren des Feldes Das mit dem Partitionieren hört sich einfacher an, als es wirklich ist. Die einfachste Lösung ist: I allokiere zwei zusätzliche Felder leftField und rightField I gehe über das sortierende Feld und füge die Elemente in leftField oder rightField ein. I sortiere leftField und rightField mit Quicksort I kopiere die beiden in das ursprüngliche Feld Der zusätzliche Speicheraufwand ist natürlich nicht gut... Informatik 2 Effiziente Sortieralgorithmen Quicksort Partitionieren des Feldes 2 Als Pivotelement wähle das Element in der Mitte. I Initialisiere zwei Iteratoren leftIt, rightIt mit den Indices des linken Randes bzw. des rechten Randes-1 I Solange nun leftIt <= rightIt I I I I I Suche von links das nächste Element welches größer oder gleich dem Pivotelement ist, so daß leftIt danach auf dieses zeigt. Suche von rechts das nächste Element welches kleiner oder gleich dem Pivotelement ist, so daß rightIt danach auf dieses zeigt. Vertausche die beiden und erhöhe leftIt um 1 und erniedrige rightIt um 1. Am Ende ist leftIt rechts von rightIt (zwischendrin ist höchstens das Pivotelement). Mache rekursiv mit dem Teil bis rightIt und dem Teil ab leftIt weiter. Informatik 2 Effiziente Sortieralgorithmen Quicksort Aufwandsabschätzung Quicksort I I I Best Case: O(n log(n)) Die Pivotelemente sind jeweils das mittlere Element der sortierten Teilliste Average Case: O(n log(n)) Lässt sich herleiten! Worst Case: O(n2 ) Die Pivotelemente sind immer an einem Rand der sortierten Teilliste. Ist dann wie Selection Sort ! Informatik 2 Effiziente Sortieralgorithmen Quicksort Bemerkungen I Um den Worst Case zu verhindern als Pivotelement häufig der Median aus dem Element am linken Rand, in der Mitte und am rechten Rand verwendet I In der Praxis spielt der Worst Case dann eigentlich keine Rolle und Quicksort ist auch wirklich “quick”. Informatik 2 Effiziente Sortieralgorithmen Mergesort Mergesort 1. Wenn die Folge 0 oder 1 Elemente enthält, ist sie schon sortiert. 2. Teile die unsortierte Folge in 2 ungefähr gleich große Teillisten. (Hier wird extra Speicher benötigt !) 3. Sortiere diese Teilfolgen rekursiv mit Mergesort. 4. Verschmelze (Merge) die dann sortierten Teilfolgen zu einer einzelnen sortierten Liste. Informatik 2 Effiziente Sortieralgorithmen Mergesort Mergesort Informatik 2 Effiziente Sortieralgorithmen Mergesort Der merge Schritt Der merge Schritt vereint zwei jeweils sortierte Folgen zu einer einzelnen sortierten Folgen I Vergleiche die aktuellen Elemente der beiden Folgen I Das kleinere füge an die aktuelle Position der Ergebnisfolge an I Schalte zum nächsten Element der “Gewinner”folge und Ergebnisfolge weiter I Wurde eine der Eingabefolgen vollkommen eingefügt, fülle die Ergebnisfolge mit dem Rest der anderen Folge auf. Informatik 2 Effiziente Sortieralgorithmen Mergesort Aufwandsabschätzung Mergesort I I I Best Case: O(n log(n)) Average Case: O(n log(n)) Worst Case: O(n log(n)) Informatik 2 Effiziente Sortieralgorithmen Bucketsort Bucketsort Hier ist der Trick, daß nicht in place sortiert wird, sondern zusätzliche “buckets” verwendet werden (häufig genauso viele wie es Elemente in der Folge gibt): I Erstelle ein Array von “buckets” (als Listen implementiert), wobei jeder für einen eigenen Bereich zuständig ist. I Füge jedes Element der Liste in den dafür passenden “bucket” I Sortiere jeden “bucket” (z.B. Insertion sort such schon während dem vorherigen Schritt) I Füge die “buckets” in der richtigen Reihenfolge zusammen Informatik 2 Effiziente Sortieralgorithmen Bucketsort Aufwandsabschätzung Bucketsort Bei n Elementen und n “buckets” I Best Case: O(n) Jeweils ein Element pro “bucket” I Average Case: O(n) I Worst Case: O(n2 ) (bei Insertion Sort) Alles geht in einen Bucket. Informatik 2 Strategien Backtracking Backtracking Backtracking ist einfach: I Basiert auf dem Trial and Error Prinzip I Es wird versucht eine Teillösung schrittweise zu einer Gesamtlösung auszubauen. I Ist absehbar, daß eine Teillösung nicht weiter führt wird der letzte Schritt zurückgenommen und eine alternative Teillösung probiert. I So werden alle Lösungswege ausprobiert. I Wird am einfachsten rekursiv programmiert. Informatik 2 Strategien Backtracking Pseudocode (aus Wikipedia) Funktion FindeLoesung (Stufe, Vektor) I wiederhole, solange es noch Teil-Lösungsschritte gibt: I I wähle neuen Teil-Lösungsschritt falls Wahl gültig I I I erweitere Vektor um Wahl falls Vektor vollsändig : return true; ansonsten: falls FindeLoesung(Stufe+1, Vektor) return true sonst mache Wahl rückgängig Kein neuer Teil-Lösungsschritt mehr: return false Informatik 2 Strategien Backtracking Beispiel 1: Labyrinth I Es soll der Weg zum Ausgang gefunden werden (ohne Karte natürlich). I Gibt es mehr als einen möglichen Weg wähle einen aus und gehe diesen weiter. I Bei einer Sackgasse gehe zurück zur letzten Kreuzung und nimm dort einen noch nicht gegangenen Weg. I Wurden an dieser Kreuzung schon alle Wege gegangen, gehe zu der Kreuzung davor zurück und wiederhole das Spiel I Irgendwann wird man dann am Ausgang ankommen. I Mit etwas Pech hat man davor jeden einzelnen Meter des Labyrinths kennengelernt. Informatik 2 Strategien Backtracking Beispiel 2: Damenproblem I I Setze n Damen auf ein n × n Brett, so daß keine geschlagen werden kann. 2 Es gibt insgesamt nn Stellungen von denen die meisten keinen Sinn machen. I Setze eine Dame (wenn nicht schon alle gesetzt sind) an eine Stelle an der sie nicht geschlagen werden kann. I Gibt es keine solche Stelle, setze die zuvor gesetzte Dame an eine andere Stelle (wenn das nicht geht die vorvorletzte usw.) I für n = 8 gibt es 92, für n = 12 gibt es 14200 Lösungen. Informatik 2 Strategien Backtracking Beispiel 3: Rucksackproblem I Eines der klassischen NP-vollständigen Probleme I Gegeben ist ein Rucksack mit Tragfähigkeit B I Außerdem N Gegenstände mit Werten und Gewichten I Gegenstände sollen so ausgewählt werden, daß der Gesamtwert maximal wird, aber das Gesamtgewicht die Tragfähigkeit des Rucksacks nicht überschreitet. I "Kreuzung": Gegenstand aufnehmen oder nicht, dann auf zum nächsten. I "Sackgasse": Rucksack ist voll bzw. alle Gegenstände drin. Nicht vergessen den Wert mit dem aktuellen Optimum zu vergleichen. Informatik 2 Strategien Backtracking Aufwand I Sehr häufig wird der gesamte Lösungsraum durchschritten. I Das bedeutet dann oft eine exponentielle Laufzeit (das Rucksackproblem z.B. ist in O(2n ), da für jeden Gegenstand ausprobiert werden muss, ob er drin ist oder nicht) I Ab und zu kann man Lösungen von vornherein ausschließen (siehe z.B. Damenproblem). Informatik 2 Strategien Dynamische Programmierung Dynamische Programmierung I I I I Optimierungsproblem: Bestimme zu einer gegebenen Zielfunktion, den “Wert” zu dem diese Funktion maximal (oder minimal) wird. Kann für Optimierungsprobleme eingesetzt werden, das sich aus vielen gleichartigen Teilproblemen besteht und eine optimale Lösung sich aus optimalen Lösungen der Teilprobleme zusammensetzt. Also: Bestimme die optimalen Lösungen der kleinsten Teilprobleme direkt und setze diese zur Lösung eines nächstgrößeren Teilproblems zusammen (usw.). Einmal berechnete Teilergebnisse werden in einer Tabelle gespeichert, damit sie nicht nochmal berechnet werden müssen, falls sie benötigt werden. (Vermeidet unnötige Rekursionen) Informatik 2 Strategien Dynamische Programmierung Beispiel: Noch mal Rucksackproblem I Sei B die Gewichtsschranke, wi der Wert von Gegenstand i, und gi das Gewicht, n Anzahl. I R(i, j): maximaler Nutzwert bei maximalem Gesamtgewicht j unter Nutzung der Gegenstände i, . . . n, gesucht ist also R(1, B) I R(i, j) lässt sich wie folgt berechnen: Ist gi ≤ j I I dann ist R(i, j) = max(wi + R(i + 1, j − gi ), R(i + 1, j)) sonst ist R(i, j) = R(i + 1, j) I Mit R(n + 1, 0 . . . B) = 0 und R(1 . . . n, 0) = 0 lässt sich der Algorithmus vereinfachen. I Es müssen alle n · B Werte berechnet werden: O(n · B) Informatik 2 Strategien Dynamische Programmierung Beispiel: Zahlen I Gesucht ist die Anzahl der n-stelligen Zahlen, bei denen die Summe von jeweils drei aufeinanderfolgenden Zahlen maximal 9 ist. I Alle möglichen Zahlen konkret zu bestimmen und dabei zu zählen ist viel zu aufwendig. Bei n = 20 sind es nämlich schon ca. 378 Billionen. I M(i, si+1 , si+2 ): Anzahl an Möglichkeiten für die Stellen i bis 1, wenn si+1 , si+2 die Werte an den Stellen i + 1 bzw. i + 2 sind. 10 − (s2 + s3 ) s2 + s3 ≤ 9 I Anfang: M(1, s2 , s3 ) = 0 sonst I Allgemein: M(i, si+1 , si+2 ) = 10−(si+1 +si+2 ) P M(i − 1, j, si+1 ) si+1 + si+2 ≤ 9 j=0 0 I Der gesuchte Wert ist dann sonst 9 P j=1 M(n − 1, j, 0). Informatik 2 Strategien Greedy Algorithmen Greedy Algorithmen I Baue schrittweise eine Lösung auf, indem in jedem Schritt der momentan "profit"reichste Weg gegangen wird (womit das Gesamtproblem verkleinert wird). I Dabei wird gehofft, daß die Wahl lokaler Optima auch zu einem globalen Optimum führt. I Es wird das Betrachten aller Möglichkeiten vermieden, dafür ist die Lösung aber häufig nicht optimal, gibt aber oft eine gute Näherung der optimalen Lösung I Wenn die optimale Lösung für ein Problem so berechnet werden kann, ist es üblicherweise die schnellste Methode. Informatik 2 Strategien Greedy Algorithmen Münzproblem I I Ermittele die kleinste Anzahl an Cent-Münzen für einen bestimmten Betrag Mit 50, 20, 10, 5, 2, 1 Cent Münzen funktioniert die gierige Methode: I I I I I Nimm die größtmögliche Münze, die kleiner als der Betrag ist. Wiederhole das Spiel mit dem Restbetrag so lange bis der Zielbetrag erreicht ist. Mit 1, 7, 10 Cent Münzen würde er scheitern (bei 14 würde er 10+1+1+1+1, statt 7+7 liefern) (Es wird z.B. kritisch, wenn 2*v[i] > v[i+1] und 2*v[i] != v[i+1] + v[j]) Es muss für jede Münze M gelten: Ein Betrag B > C kann ohne Nutzung von C nicht optimal zusammengesetzt werden. Informatik 2 Suchen und Bäume Einleitung Suchalgorithmen I Ziel ist es aus einer Menge von Daten ein oder mehrere Elemente anhand eines Schlüssels zu finden I Ist die Menge (Array / verkettete Liste) unsortiert, so bleibt uns nichts anderes übrig als alle Elemente anzuschauen → O(n) I Bei sortierten Mengen kann man evtl. schon früher abbrechen, aber muß evtl. trotzdem bis ans Ende suchen I Hat man direkten Zugriff auf die Elemente (Arrays) geht es noch besser, über Intervallschachtelung. I Wir suchen und zeigen im Folgenden zur Vereinfachung nur die Schlüssel, welche Integerwerte sein werden Informatik 2 Suchen und Bäume Einleitung Binäre Suche I I Es soll in einer Menge mit n Elementen gesucht werden. Wir schauen uns das mittlere Element an: I I I I Ist der gesuchte Wert kleiner als das mittlere Element, dann beschaue im nächsten Schritt nur noch die Menge links von m Ist der gesuchte Wert größer, dann nimm im nächsten Schritt die rechte Teilmenge (evtl.) ist es der gesuchte Wert, so wurde die Stelle schon gefunden Mit dem Mastertheorem sieht man, daß die Laufzeit in O(log n) liegt. Informatik 2 Suchen und Bäume Einleitung Binäre Suche Code 1 function B IN S EARCH(M, start, ende, key ) if ende ≥ start then mitte ← b(ende + start)/2c if M[mitte] = key then return mitte else if M[mitte] < key then return BinSearch(M, mitte + 1, ende, key ) else return BinSearch(M, start, mitte − 1, key ) end if else return -1 end if end function Informatik 2 Suchen und Bäume Einleitung Binäre Suche Code 1 - C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 int binSearch (int field[], int start, int end, int key) if (end >= start){ int mid = (end + start) / 2; if (field[mid] == key){ return mid; } else if (field[mid] < key){ return binSearch (field, mid+1, end, key); } else { return binSearch (field, start, mid-1, key); } } else { return -1; }} Informatik 2 Suchen und Bäume Einleitung Binäre Suche Code 2 function B IN S EARCH(M, start, ende, key ) if ende > start then mitte ← b(ende + start)/2c if M[mitte] < key then return BinSearch(M, mitte + 1, ende, key ) else return BinSearch(M, start, mitte, key ) end if else if M[start] = key then return start else return -1 end if end if end function Informatik 2 Suchen und Bäume Einleitung Interpolationssuche I Es geht noch besser, wenn man mit den Elementen rechnen kann und die Werte ungefähr gleichverteilt sind · (k − s)) (also a[k ] ≈ M[s] + M[e]−M[s] e−s I Statt der Mitte wird nun der ungefähre Index durch lineare Interpolation berechnet: m= I e−s · (W − M[s]) + s M[e] − M[s] Falls die Annahmen stimmen liegt die Laufzeit in O(log log n), wenn nicht ist es langsamer als Binäre Suche Informatik 2 Suchen und Bäume Einleitung Interpolationssuche Code function I NTERPOL S EARCH(M, start, ende, key ) if ende > start j then k −M[start]) mitte ← start + (ende−start)·(key M[ende]−M[start] if (mitte < start) ∨ (mitte > ende) then return -1 else if M[mitte] = key then return mitte else if M[mitte] < key then return InterpolSearch(M, mitte + 1, ende, key ) else return InterpolSearch(M, start, mitte − 1, key ) end if else if M[ende] = key then return start else return -1 end if Informatik 2 Suchen und Bäume Einleitung Interpolationssuche - C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int interpolSearch(int field[],int start,int end,int key){ if (end > start){ int mid = (end - start) * (key - field[start]); mid /= (field[end] - field[start]); mid += start; if ((mid < start) || (mid > end)) { return -1; } else if (field[mid] == key) { return mid; } else if (field[mid] < key) { return interpolSearch (field, mid+1, end, key); } else { return interpolSearch (field, start, mid-1, key); } } else { // end == start if (field[end] == key) { return start; } else { return -1; }}} Informatik 2 Suchen und Bäume Einleitung I Auf sortierten Arrays ist alles fein, allerdings ist der lineare Aufwand zum Einfügen und Löschen von Elementen zu hoch, wenn dies häufig (im Vergleich zum Suchen) vorkommt. I Listen sind da besser, aber da geht die Suche per Intervallschachtelung nicht, weil der direkte wahlfreie Zugriff auf ein Element fehlt I Suchbäume sollen dabei helfen. Nicht nur die Suche auch das Einfügen und Löschen soll damit in O(log n) ablaufen Informatik 2 Suchen und Bäume Einleitung Nicht diese Bäume! Informatik 2 Suchen und Bäume Einleitung Definition Ein Baum ist eine Menge von Knoten und Kanten, bei denen es zwischen zwei Knoten genau einen Weg gibt. 42 14 33 -3 13 9 52 54 Eine Menge von Bäumen wird Wald genannt. Informatik 2 Suchen und Bäume Einleitung Bezeichnungen 42 14 33 -3 I I I I I I I 13 9 52 54 42 ist der Wurzelknoten 33, -3, 9 sind Kindknoten von 14 14 ist der Elternknoten zu 33, -3, 9 14, 13, 52 sind Geschwisterknoten 33, -3, 9, 54, 52 sind Blätter (haben keine Kindknoten) 13, 54 bilden einen Unterbaum (mit Wurzel 13) zu Knoten 42 Die Höhe des Baumes ist die Anzahl der “Generationen” (hier 3) Informatik 2 Suchen und Bäume Suchen in Bäumen Suchen in Bäumen Suchen kann man mittels I Tiefensuche (Depth-First Search): Gehe solange in die Tiefe bis es nicht mehr weitergeht, gehe dann zum nächsten noch nicht besuchten Nachbarn und wiederhole das Ganze. I Breitensuche (Breadth-First-Search): Besuche nacheinander alle Knoten eines Levels (einer Generation), bevor der nächste Level angegangen wird. Kann man dadurch implementieren, daß die Knoten eines Levels in einer Liste zwischengespeichert werden, die dann abgearbeitet wird. Beim Abarbeiten dieser Liste schreibt dann jeder Knoten seine Kinder wieder in die Liste. Ist z.B praktisch für die Visualisierung. Informatik 2 Suchen und Bäume Diverse Beispiele Man trifft häufig auf baumartige Strukturen, so lässt sich der Ausdruck (5.4 − 3.5) + 3.1 als Baum ausdrücken: + 5.4 3.1 3.5 Informatik 2 Suchen und Bäume Diverse Beispiele Entscheidungsbaum Auch bei Entscheidungsabläufen sind Bäume beliebt. Hier ein einfacher Entscheidungsbaum zur Vorhersage, ob ein Apfelbaum Früchte tragen wird aus Wikipedia: Informatik 2 Suchen und Bäume Diverse Beispiele Spielbaum Spielbäume mit allen möglichen Zügen: Informatik 2 Suchen und Bäume XML XML I XML = Extensible Markup Language I zur Darstellung hierarchisch strukturierter Daten in Textform I wird u.a. für den plattform- und implementationsunabhängigen Austausch von Daten zwischen Computersystemen eingesetzt I Als Baumknoten gibt es u.a. Elemente, die durch Startund End-Tags ausgezeichnet werden, sowie auch einfachen Text. I Hierarchie entsteht durch ineinander verschachtelte Tags Informatik 2 Suchen und Bäume XML Beispiel 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8"?> <verzeichnis> <titel>Staedteverzeichnis</titel> <eintrag> <stichwort>Genf</stichwort> <text> Genf ist der Sitz von ... </text> </eintrag> <eintrag> <stichwort>Stuttgart</stichwort> <text> Stuttgart ist eine Stadt, die ... </text> </eintrag> </verzeichnis> Informatik 2 Suchen und Bäume XML Beispiel 2 Die XML Version des Baums für den arithmetischen Ausdruck von Folie 65: 1 2 3 4 5 6 7 <ADD> <SUB> <value> 5.4 </value> <value> 3.5 </value> </SUB> <value> 3.1 </value> </ADD> Informatik 2 Suchen und Bäume Binärbäume Datenstruktur Binärbäume Am häufigsten hat man es mit Bäumen zu tun, bei denen jeder Knoten höchstens zwei Kinder hat, den sogenannten Binärbäumen. 1 typedef int DATATYPE; 2 3 4 5 6 7 8 struct Node { DATATYPE data; Node* left; Node* right; }; 9 10 typedef Node* BinaryTree; Allgemein steht da eine Liste von Kindern drin. Informatik 2 Suchen und Bäume Binärbäume Grundlegende Fkt. zu Binärbäumen 1 2 3 void initialize (BinaryTree& bt){ bt = NULL; } 4 5 6 7 bool isEmpty (BinaryTree& bt){ return (bt == NULL); } 8 9 10 11 12 13 14 15 bool isLeaf (BinaryTree& bt){ if (isEmpty (bt)){ return false; } else { return (isEmpty(bt->left) && (isEmpty (bt->right))); } } Informatik 2 Suchen und Bäume Binärbäume Als Array gespeichert 1 2 3 4 5 6 7 #define MAX_HOEHE 5 #define MAX_KNOTEN ((1 << MAX_HOEHE)-1) typedef int DATATYPE; struct Node { DATATYPE data; }; typedef Node[MAX_KNOTEN] BinaryTree; I Die einzelnen Levels werden nacheinander im Array gespeichert I Die Wurzel hat den Index 0 I Die Wurzel des linken Unterbaumes zu Knoten i ist 2i + 1, die des rechten Unterbaumes 2i + 2 Informatik 2 Suchen und Bäume Binärbäume Traversierungen Es gibt es 3 unterschiedliche Strategien zur Tiefensuche: I Preorder: I I I I Inorder: I I I I Knoten bearbeiten / überprüfen linken Unterbaum betrachten rechten Unterbaum betrachten linken Unterbaum betrachten Knoten bearbeiten / überprüfen rechten Unterbaum betrachten Postorder: I I I linken Unterbaum betrachten rechten Unterbaum betrachten Knoten bearbeiten / überprüfen Informatik 2 Suchen und Bäume Binärbäume Beispielimplementierung Preorder 1 2 3 4 5 6 7 8 9 void printPreorder (BinaryTree bt) { if (!isEmpty(bt)) { std::cout << bt->data << " "; printPreorder (bt->left); printPreorder (bt->right); } } Informatik 2 Binäre Suchbäume Einleitung Binäre Suchbäume Ein binärer Suchbaum ist ein binärer Baum für den gilt: I Für jeden Knoten sind die Werte im linken Unterbaum kleiner als der Eigene I Für jeden Knoten sind die Werte im rechten Unterbaum größer als der Eigene Bemerkungen: I Die Höhe ist im besten Fall (und im Durchschnitt) O(log(n)) bei n Knoten. I Die Suche nach einem Wert kann hier wie bei der binären Suche wohl durchschnittlich in O(log(n)) erfolgen. I Allerdings lassen sich hier Werte effizienter einfügen und wieder entfernen. Informatik 2 Binäre Suchbäume Einleitung Suchen 1 2 3 4 5 6 7 8 9 10 11 12 bool search (BinaryTree bt, DATATYPE d){ if (isEmpty (bt)) { return false; } else { if (bt->data == d){ return true; } else if (d < bt->data) { return search (bt->left, d); } else { return search (bt->right, d); }}} Informatik 2 Binäre Suchbäume Einfügen von Knoten Einfügen von Knoten Einfacher Algorithmus zum Einfügen eines neuen Knotens: I Man geht so lange den Baum herunter bis man an der richtigen Position ist I Ist der neue Knoten kleiner als (oder hat den gleichen Wert wie) der aktuelle Wurzelknoten, dann füge ihn (rekursiv) in den linken Unterbaum ein I Ist der neue Knoten größer als der aktuelle Wurzelknoten, dann füge ihn (rekursiv) in den rechten Unterbaum ein I Am Ende, wenn der aktuelle Unterbaum leer ist, wird dann der Knoten tatsächlich eingefügt (als Blatt) Die Anzahl der Schritte entspricht im dümmsten Fall der Höhe des Baumes. Im durchschnittlichen Fall liegt das in O(log(n)) ! Informatik 2 Binäre Suchbäume Einfügen von Knoten Einfügen in Binärbäume 1 2 3 4 5 6 7 8 9 10 11 12 13 void addElement (BinaryTree& bt, DATATYPE d){ if (isEmpty (bt)) { Node* add = new Node; add->data = d; add->left = NULL; add->right = NULL; bt = add; } else { if (d < bt->data){ addElement (bt->left, d); } else if (d > bt->data){ addElement (bt->right, d); }}} Informatik 2 Binäre Suchbäume Entfernen von Knoten Entfernen von Knoten Schon etwas komplizierter: I Ist der zu löschende Knoten ein Blatt, kann er einfach gelöscht werden. I Hat er ein Kind, dann wird er nach dem Löschen einfach durch dessen Teilbaum ersetzt. I Mit 2 Kindern: Der linke Teilbaum wird an die Position des gelöschten Knotens gesetzt und der rechte Teilbaum wird an diesen dann passend angehängt (geht auch symmetrisch). I Besser mit 2 Kindern: Ersetze den gelöschten Knoten durch den nächstgrößeren Knoten (im rechten Teilbaum immer nach links). Dessen evtl. rechter Unterbaum ersetzt ihn dann. Auch hier ist der Aufwand von der Höhe abhängig, also durchschnittlich in O(log(n)) ! Informatik 2 Binäre Suchbäume Entfernen von Knoten Löschen in Binärbäumen Basis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void deleteElement (BinaryTree& bt, DATATYPE d){ if (isEmpty (bt)){ return; } else if (d < bt->data){ deleteElement (bt->left, d); } else if (d > bt->data){ deleteElement (bt->right, d); } else { if (isLeaf (bt)){ delete bt; bt = NULL; } else if (bt->left == NULL) { Node* old = bt; bt = bt->right; delete old; } else if (bt->right == NULL){ Node* old = bt; bt = bt->left; delete old; } else { ... Informatik 2 Binäre Suchbäume Entfernen von Knoten Löschen in Binärbäumen 1.Variante } else { Node* tempRight = bt->right; Node* old = bt; bt = bt->left; Node* it = bt; while (it->right != NULL) { it = it->right; } it->right = tempRight; delete old; 1 2 3 4 5 6 7 8 9 10 11 }}} Informatik 2 Binäre Suchbäume Entfernen von Knoten Loeschen in Binaerbaeumen 2.Variante } else { Node* it = bt->right; Node* itParent = bt; while (it->left != NULL){ itParent = it; it = it->left; } bt->data = it->data; Node* old = it; itParent->left = it->right; delete old; 1 2 3 4 5 6 7 8 9 10 11 12 }}} Informatik 2 Binäre Suchbäume Entfernen von Knoten Suchpfadlänge I Die Suchpfadlänge für einen einzelnen Knoten ist die Zahl an besuchten Knoten bis er gefunden wurde. Das ist der Abstand des Knotens zur Wurzel+1 (die Wurzel selbst) I Die innere Suchpfadlänge S(T) eines Baumes T ist die Summe der Suchpfadlängen aller Knoten I Für einen Baum T mit n Knoten und linkem Teilbaum Tl und rechtem Teilbaum Tr gilt dabei: S(T ) = S(Tl ) + S(Tr ) + n I Die durchschnittliche Suchpfadlänge eines Baumes mit n Knoten ist 1 S(T ) n I Nun interessiert die erwartete innere Suchpfadlänge. D.h. welche innere Suchpfadlänge kann man bei einen zufällig erzeugten Baum erwarten ? I Ohne Beweis: Das ist ca. ES(n) = 1.386n log2 n − 0.846n + O(log n) I Damit ist die zu erwartende durchschn. Suchpfadlänge: ES(n) ∈ O(log n) n Informatik 2 AVL-Bäume Motivation Motivation I Durch Löschen oder "ungeschicktes" Einfügen (z.B. 1,2,3,4,5,6), kann der Baum entarten, d.h. es entstehen lange Äste. I In diesen Fällen liegt dann der Aufwand zum Suchen, Einfügen und Löschen bei jeweils O(n). I Ergo muss eine solche Entartung verhindert werden durch zusätzliche Operationen, die aber nicht die Suchbaum Eigenschaft verletzen und in O(1) ablaufen. I Dabei helfen “Rotationen” Informatik 2 AVL-Bäume Rotationen Rotation rechts D B B D E A C A C E Informatik 2 AVL-Bäume Rotationen Rotation rechts 1 2 3 4 5 6 7 void rotationRight (Tree& subtree) { Node* temp = subtree->left; subtree->left = (subtree->left)->right; temp->right = subtree; subtree = temp; } Informatik 2 AVL-Bäume Rotationen Rotation links B D D B A E C E A C Informatik 2 AVL-Bäume Rotationen Rotation links 1 2 3 4 5 6 7 void rotationLeft (Tree& subtree) { Node * temp = subtree->right; subtree->right = (subtree->right)->left; temp->left = subtree; subtree = temp; } Informatik 2 AVL-Bäume AVL-Bäume AVL-Bäume I Name kommt von den Entdeckern Adelson-Velski und Landis (1962) I Jeder Knoten erhält ein zusätzliches Attribut: die Balance I Balance eines Knotens = Höhe des rechten Unterbaums Höhe des linken Unterbaums I Erlaubt sind nur Balancen von -1, 0, +1 für die Knoten des Baumes I Balancen von -2 oder +2 können durch Rotationen behoben werden. Informatik 2 AVL-Bäume AVL-Bäume Datenstruktur 1 typedef int DATATYPE; 2 3 4 5 6 7 8 9 struct Node { DATATYPE data; int balance; Node* left; Node* right; }; 10 11 typedef Node* AVLTree; Informatik 2 AVL-Bäume Einfügen Einfügen I Das Einfügen funktioniert vorerst wie beim normalen Suchbaum. I Das einfügte Element hat die Balance 0. Für die direkten Vorgänger ändert sich die Balance I I I I um +1, wenn der neue Knoten im rechten Unterbaum um -1, wenn der neue Knoten im linken Unterbaum eingefügt wurde Wurde die Balance zu: I I I 0: Höhe blieb gleich. Die Balance der darüberliegenden Knoten muß nicht mehr angepasst werden. -1 oder 1: Die Höhe des Unterbaums hat sich um 1 erhöht. Die Balance muß auch im direkten Vorgänger angepasst werden. -2 oder +2: Es muss per Rotationen repariert werden. Informatik 2 AVL-Bäume Einfügen Einfügen Reparatur I Balance der Wurzel (D) ist -2, das linke Kind (B) hat eine Balance von -1: Rechtsrotation um D D B B D E C A A C E Informatik 2 AVL-Bäume Einfügen Einfügen Reparatur II Balance der Wurzel (F) ist -2, das linke Kind (B) hat eine Balance von +1: Linksrotation um B, dann Rechtsrotation um F. (Es kann auch E um eins höher als C sein, aber das ändert nicht viel) F F B D D A B B G E A C F G E A E C D C G Informatik 2 AVL-Bäume Einfügen Einfügen Reparatur III/IV Das Vorgehen bei einer Balance von +2 für den aktuell bearbeiteten Knoten ist gespiegelt zu den vorherigen Fällen: I Rechtes Kind hat Balance +1: Linksrotation um aktuellen Knoten I Rechtes Kind hat Balance -1: Rechtsrotation um das rechte Kind und dann Linksrotation um den aktuellen Knoten. In jedem der Fälle hat dann der aktuelle Knoten hinterher die Balance 0, und die Balance der darüberliegenden Knoten ändert sich somit nicht mehr. Informatik 2 AVL-Bäume Löschen Löschen I Löschen funktioniert bis auf das Balancieren wie beim Suchbaum. I Auch hier müssen auf dem Pfad der direkten Vorgänger die Balancen angepasst werden. Wurde die Balance zu: I I I I -1 oder 1: (vorher 0) Höhe blieb gleich, nur ein Unterbaum ist kürzer geworden. Die Balance der darüberliegenden Knoten muß nicht mehr angepasst werden. 0: Der (ehemals) höhere Unterbaum wurde gekürzt. Damit wurde die Höhe des aktuellen Baums um 1 reduziert! Die Balance muß auch im direkten Vorgänger angepasst -2 oder +2: Es muss per Rotationen repariert werden. Informatik 2 AVL-Bäume Löschen Löschen Reparatur I/II/IV/V Folgende Fälle sind mit den gleichen Rotationen zu behandeln wie im Falle des Einfügens: I Balance der Wurzel ist -2, Balance des linken Kindes ist -1 ⇒ Rechtsrotation um Wurzel. I Balance der Wurzel ist -2, Balance des linken Kindes ist +1 ⇒ Linksrotation um linkes Kind, Rechtsrotation um Wurzel. I Balance der Wurzel ist 2, Balance des rechten Kindes ist +1 ⇒ Linksrotation um Wurzel. I Balance der Wurzel ist 2, Balance des linken Kindes ist -1 ⇒ Rechtsrotation um rechtes Kind, Linksrotation um Wurzel. Allerdings muß nun der direkte Vorgänger angepasst werden ! Informatik 2 AVL-Bäume Löschen Löschen Reparatur III/VI Balance der Wurzel (D) ist -2, Balance des linken Kindes (B) ist 0: Rechtsrotation um Wurzel (symmetrisch für +2) D B B D E A A C E C Höhe hat sich nicht geändert: Die direkten Vorgänger müssen nicht mehr angepasst werden. Informatik 2 Heaps Einleitung Heaps I Heaps sind eine abstrakte Datenstruktur, die Mengen speichert bei denen jedes Element einen Schlüssel hat, der die Priorität festlegt (Das Element kann auch selbst der Schlüssel sein) I Wird zumeist mit Bäumen realisiert. I Es muß die Heapbedingung erfüllt sein: Der Schlüsselwert aller Kinder muß kleiner (bzw. größer) sein, als der Schlüsselwert des Elternknotens (je nachdem ob oben das Maximum oder Minimum stehen soll, es also ein Max-Heap oder ein Min-Heap wird). I Eignet sich hervorragend für Prioritätswarteschlangen. Informatik 2 Heaps Übliche Operationen Übliche Operationen Für einen Min-Heap (mit minimalem Schlüssel als Wurzel) I create: Erstelle einen leeren Heap. I insert: Füge einen neuen Knoten in den Heap hinzu, so daß die Heapbedingung erfüllt ist I find-min: Liefere das minimale Element zurück I delete-min: Entferne das Element mit dem geringsten Schlüssel I decrease-key: Reduziere den Wert des Schlüssels eines Knotens Informatik 2 Heaps Übliche Operationen Beispiel eines Heaps als Binärbaum Quelle: Wikipedia Informatik 2 Heaps Implementation eines Heaps mit Binärbäumen Implementation 1 I Binärbäume lassen sich auch hervorragend als Array darstellen, wenn eine maximale Anzahl vorher bekannt ist. I Die Kinder eines Knotens an Position i stehen dann an Position 2i + 1 und 2i + 2 (Wurzel ist an Position 0). I Umgekehrt ist der Elternknoten eines Kindes an Position i dann an Position i−1 2 Informatik 2 Heaps Implementation eines Heaps mit Binärbäumen Implementation 2 insert: I das einzufügende Element wird im Array ans Ende hinzugefügt (im Baum ist es dann der rechte Nachbar vom rechtesten Knoten auf dem untersten Level) I Nun müssen die Heapbedingungen überprüft werden: Vergleiche das soeben eingefügte Element mit seinem Elternknoten. Ist der Schlüssel dort kleiner, ist alles in Ordnung. Wenn nicht, dann werden die beiden vertauscht und es geht weiter (einen Level höher) bis entweder die Heapbedingung erfüllt wurde oder die Wurzel erreicht wurde (Bubble up). I Durch diese Methode entartet der Baum nicht, so daß die Komplexität der Operation O(log n) ist. Informatik 2 Heaps Implementation eines Heaps mit Binärbäumen Implementation 3 delete-min: I Ersetze die Wurzel (an der sich das Minimum befindet) mit dem letzten Element I Nun müssen die Heapbedingungen überprüft werden: Vergleiche das soeben eingefügte Element mit seinen beiden Kindern. Sind die Schlüssel der beiden größer, ist alles in Ordnung. Wenn nicht, dann vertausche den Knoten mit dem Kind, das den kleinsten Schlüssel und es geht weiter (einen Level tiefer) bis entweder die Heapbedingung erfüllt wurde oder die Blattebene erreicht wurde (Bubble down). I Durch diese Methode entartet der Baum nicht, so daß die Komplexität der Operation O(log n) ist. Informatik 2 Heaps Implementation eines Heaps mit Binärbäumen Implementation 4 decrease-key: I Finde den Knoten zu dem passenden Element. I Verringere den Schlüsselwert im Knoten I Evtl. sind die Heapbedingungen verletzt. Das Vorgehen ist das gleiche wie beim Einfügen (Bubble up). I Die Komplexität ist abhängig von der Knotensuche: O(Suche + log n). I Um zu einem Element schnell den Index zu finden, sollte man noch eine zusätzliche Datenstruktur hinzunehmen, die einem Element den Index im Array zuweisen. Ist der zu speichernde Wert ein eindeutiger Integer, so geht das ganz einfach mit einem zusätzlichen Array. Informatik 2 Heaps Implementation eines Heaps mit Binärbäumen Christmas tree and heap of presents Quelle: http://xkcd.com/835/ Informatik 2 Heaps Heapsort Heapsort Ein Array lässt sich mit einem Max-Heap auch sortieren I Stelle Heapbedingungen her. Z.B. mache eine Bubble-up für jedes Element. Aufwand maximal O(n log n), tatsächlich sogar O(n) I Das oberste ist beim Max-Heap das Größte Element. Statt es mit dem letzten Element zu überschreiben, vertausche es mit diesem und verringere die Heapgröße um 1 ohne den Speicher freizugeben. Damit ist das größte Element gerade außerhalb des Heapbereichs. I Mache dies solange bis der Heap nur noch ein Element hat. Aufwand dafür liegt in O(n log n) I Damit lässt sich ein Array in jedem Fall in O(n log n) sortieren ohne zusätzlichen Speicher zu benötigen ! I In der Praxis so aber langsamer als Quicksort. Informatik 2 Graphen Kurze Einführung Definition Ein Graph ist ein Tupel (V,E) mit I V ist die Menge von Knoten (Vertices) I E ist die Menge der Kanten (Edges), welche Knoten miteinander verbindet und somit eine Teilmenge aus V × V. Ein Graph kann I ungerichtet sein: es zählt nur, ob eine Kante zwischen 2 Knoten existiert oder nicht. I gerichtet sein: es kommt auf Start- und Zielknoten an. So kann es eine Kante von A nach B und von B nach A geben, die unterschiedlich sind. I gewichtet sein: jeder Kante wird ein Wert zugeordnet. Ein Baum ist also ein spezieller Graph. Informatik 2 Graphen Kurze Einführung Begriffe I Teilgraph eines Graphen G: enthält nur Knoten und Kanten die auch in G vorkommen (Teilmenge) I Weg / Knotenfolge: Eine Folge von Knoten v1 , . . . , vn bei der zwei aufeinanderfolgende Knoten durch eine Kante verbunden sind. I Pfad: Ein Weg, in der alle Knoten unterschiedlich sind I Zyklus: Ein Weg, bei dem v1 = vn ist I zyklischer / azyklischer Graph: ein gerichteter Graph, der mind. einen / keinen Zyklus enthält I zusammenhängender Graph: zwischen zwei beliebigen Knoten existiert ein Weg. Informatik 2 Graphen Kurze Einführung Ein Beispiel eines gerichteten Graphen V = {A, B, C, D, E, F } E = {(A, B), (B, C), (C, E), (D, B), (E, D), (E, F ), } Informatik 2 Graphen Kurze Einführung Ein praktisches Beispiel V = {Frankfurt, Stuttgart, Mannheim, . . . } E = {(Stuttgart, Nuernberg), (Karlsruhe, Mannheim), . . . } g((Frankfurt, Mannheim)) = 85, . . . Informatik 2 Graphen Repräsentation von Graphen Inzidenzmatrix I Eine Matrix (2-dimensionales Array) der Größe n × m bei n Knoten und m Kanten. I Ist der Knoten i an der Kante j beteiligt, so gibt es in der j.-ten Zeile und i.-ten Spalte einen Eintrag ungleich 0 I Bei einem ungerichteten Graphen ist der Wert 1 I Bei gerichteten Graphen, kommt es darauf an, ob der Knoten der Start oder das Ziel ist. Im ersten Fall ist der Wert 1, beim zweiten Fall -1. I Jede Spalte hat also maximal 2 Werte ungleich 0 (genau 2, wenn der Knoten angebunden ist) I Lohnen sich, wenn es wenige Kanten gibt. Informatik 2 Graphen Repräsentation von Graphen Adjazenzmatrix I Eine Matrix der Göße n × n bei einem Graph mit n Knoten und m Kanten. I Für jede Kante von Knoten i zu Knoten j, wird ein Wert in der i.-ten Zeile und j.-ten Spalte eingetragen. I Bei Graphen, die nicht gewichtet sind, ist der Wert 1, wenn es eine Kante zwischen beiden gibt und 0, wenn es keine gibt. I Dieser Wert kann bei einem kantengewichteten Graphen das Gewicht sein, wenn es eine Kante zwischen beiden gibt. Gibt es keine Kante muß es ein Wert sein, der nicht als Gewicht vorkommt (häufig 0). I Bei ungerichteten Graphen ist die Adjazenzmatrix symmetrisch Informatik 2 Graphen Repräsentation von Graphen Adjazenzliste I I I 1 2 3 4 5 6 7 8 9 10 Eine Liste aller Knoten (bei bekannter Anzahl an Knoten als Array implementiert), Jeder Knoten besitzt eine Liste von Nachfolgern bzw. von Kanten Bei gewichteten Graphen kommt noch für jeden Nachfolger ein Gewicht dazu struct Node { Edge* edges; //Node* nextNode; }; struct Edge { int targetNode; double weight; Edge* nextEdge; }; Node adjList[numberOfNodes]; Informatik 2 Graphen Algorithmen Suche des kürzesten Wegs I Problem: Wir haben einen gewichteten Graphen mit positiven Kantengewichten und es ist der kürzeste Weg von einem Startknoten zu einem Endknoten (oder allen anderen) gesucht. I Die Lösung die vorgestellt wird, wurde von Edsger Dijkstra 1959 veröffentlicht, und wird daher auch gerne als Dijkstra Algorithmus bezeichnet. I Gehört zu den Greedy Algorithmen, die auch wirklich immer die optimale Lösung finden. Informatik 2 Graphen Algorithmen Idee I Speichere für jeden Knoten den Abstand vom Startknoten und den Vorgänger, sowie ob er schon besucht wurde oder nicht. I Am Anfang ist der Abstand “unendlich” (außer beim Startknoten, da ist er 0) und es gibt keinen (gültigen) Vorgänger. Solange es noch nicht besuchte Knoten gibt, wähle den mit dem geringsten Abstand aus I I I Berechne für alle noch nicht besuchten Nachfolger dieses Knoten die Summe des Kantengewichts und des Abstandswertes Ist dieser Wert für einen Nachfolger kleiner als der bisherige Abstand, so aktualisiere den Abstandswert und den Vorgänger Informatik 2 Graphen Algorithmen Umsetzung 1 I I Es wird also schrittweise ein Baum aufgebaut, und ist ein Knoten mit seinem Vorgänger mal in diesem Baum, bleibt das auch so. Die Knoten lassen sich in drei Kategorien einteilen: I I I Schon eingefügte Knoten (Closed List) Knoten die sich über schon eingefügte Knoten erreichen lassen (Randknoten) Rest (noch nicht “entdeckte” Knoten) I Explizit verwaltet werden üblicherweise nur die Randknoten. I Die benötigten Operationen für die Menge der Randknoten ist: Hinzufügen (insert), Minimum löschen (delete-min), Knotenschlüssel verringern (decrease-key) Informatik 2 Graphen Algorithmen Umsetzung 2 function D IJKSTRA(graph, start) Initialisiere Abstand und Vorgaenger Abstand[start] = 0; Initialisiere Randknoten insert (Randknoten, start, 0) while IstNichtLeer (Randknoten) do minKnoten ← delete − min(Randknoten) for all nachfolger von minKnoten do alternativ ← Abstand[minKnoten] + Distanz(minKnoten, nachfolger ) if alternativ < Abstand[nachfolger ] then Abstand[nachfolger ] ← alternativ if IstEnthalten (Randknoten, nachfolger) then decrease-key (Randknoten, nachfolger, alternativ) else insert (Randknoten, nachfolger, alternativ) end if end if end for end while end function Informatik 2 Graphen Algorithmen Aufwandsabschätzung Anzahl der Operationen für einen zusammenhängenden Graphen mit n Knoten und m Kanten im worst case: I insert: n mal (jeder Knoten kommt einmal in die Randmenge) I delete-min: n mal (jeder Knoten wird einmal rausgeholt) I decrease-key: m mal (für jede Kante) Bei Implementation der Randmenge als Liste mit n Elementen: I insert: O(1) (an den Anfang einfügen) I delete-min: O(n) (suche Minimum aus allen Elementen) I decrease-key: O(1) bei Direktzugriff auf Element I Insgesamt: O(n · n + n · 1 + m · 1) = O(n2 + n + m) Bei Implementation der Randmenge als MinHeap: I insert: O(log n) I delete-min: O(log n) I decrease-key: O(log n) I Insgesamt: O(n · log n + n · log n + m log m) = O((2n log n + m log n) Informatik 2 Graphen Algorithmen A* Algorithmus I Soll den schnellsten Weg zwischen zwei Knoten auch möglichst schnell finden. I Der Dijkstra Algorithmus weiß außer dem Namen nichts vom Ziel I Dementsprechend kann es passieren, daß er vom Startknoten die kürzesten Strecken zu allen anderen Knoten berechnet, bevor der Zielknoten dran ist. I Um dies einzuschränken soll die verbleibende Distanz zum Zielknoten mit berücksichtigt werden. I Ansonsten fast wie der Dijkstra Algorithmus Informatik 2 Graphen Algorithmen Heuristik I Die Kosten eines Knotens sind: f (x) = g(x) + h(x) I g(x): Bisheriger Abstand des Knotens x vom Startknoten I h(x): Heuristik zur Abschätzung der Distanz vom Knoten x zum Zielknoten I Dijkstra: h(x) = 0 I Die Heuristik h(x) muß zulässig (admissible) sein, d.h. sie überschätzt nie die Distanz zum Zielknoten. I Der Abstand über die "Luftlinie" ist so eine Heuristik. I f (x) ist aber nicht mehr unbedingt der bisher kürzeste Weg zu x !!! Informatik 2 Graphen Algorithmen Monotonie I Eine weitere erstrebenswerte Eigenschaft einer Heuristik ist die Monotonie I ∀(x, y ) ∈ E : h(x) ≤ d(x, y ) + h(y ) d(x, y ) sei der wirkliche Abstand von x nach y . I Jede monotone Heuristik ist auch zulässig. I Luftlinienabstand ist monoton. I Ist die gewählte Heuristik nicht monoton, so müssen Knoten aus der Closed List evtl. noch einmal besucht werden. I Bei einer monotonen Heuristik kann einfach der Dijkstra mit der Kostenfunktion f (x) durchgezogen werden.