Vorlesung Grundlagen der Informatik Dr. Frank Sausen Skript und Folien: Prof. Dr. Wolfgang Ertel 6. Oktober 2008 Hochschule Ravensburg−Weingarten Technik | Wirtschaft | Sozialwesen c W. Ertel 1 Inhaltsverzeichnis Literaturverzeichnis 5 Algorithmen auf Graphen – Ergänzung 6 2 Literaturverzeichnis [1] Wikipedia – Die freie Enzyklopädie. www.wikipedia.org. [2] F. Naumann. Vom Abakus zum Internet. Wissenschaftliche Buchgesellschaft, 2001. Geschichte der Informatik. [3] H. Matis. Die Wundermaschine. mitp-Verlag, 2002. Geschichte des Computers. [4] W. de Beauclair. Rechnen mit Maschinen – eine Bildgeschichte der Rechentechnik. Vieweg Verlag, 1968. Die Lektüre dieses Bilderbuches lohnt sich! 3 [5] D. Shasha and C. Lazere. Out of Their Minds: The Lives and Discoveries of 15 Great Computer Scientists. Copernicus/ An Imprint of Springer-Verlag, 1995. Sehr unterhaltsam und informativ. [6] V. Claus and A. Schwill. Duden Informatik. Bibliographisches Institut & F.A. Brockhaus AG, 1988. Ein gutes Nachschlagewerk zur Informatik allgemein. [7] P. Rechenberg and G. Pomberger. Informatik-Handbuch. Hanser Verlag, 2001. [8] C. Horn and O. Kerner. Lehr- und Übungsbuch Informatik, Band 1: Grundlagen und Überblick. Fachbuchverlag Leipzig, 2001. [9] T.H. Cormen, Ch.E. Leiserson, and R. L. Rivest. Introduction to Algorithms. MIT Press, Cambridge, Mass, 1994. Sehr gute Einführung in die Analyse von Algorithmen. [10] N. Wirth. Algorithmen und Datenstrukturen. Teubner-Verlag, Stuttgart, 1983 (3. Auflage). Ein Klassiker, vom Erfinder der Sprache PASCAL. 4 [11] R. Sedgewick. Algorithmen. Addison-Wesley, Bonn, 1995. Übersetzung d. engl. Originals, empfehlenswert. [12] U. Hedtstück. Einführung in die Theoretische Informatik. Oldenbourg Verlag, München, 2007. Guter Überblick über Formale Sprachen und Automaten. [13] P. Tittmann. Graphentheorie. Fachbuchverlag Leipzig, 2003. Sehr gutes Buch mit vielen Beispielen. Leider fehlen die Wegesuchalgorithmen. [14] S. Krumke and H. Noltemeier. Graphentheoretische Konzepte und Algorithmen. Teubner Verlag, 2005. Exakt und gleichzeitig anschaulich. [15] Paul E. Black (Ed.). Dictionary of Algorithms and Data Structures [online]. National Institute of Standards and Technology, 2004. http://www.nist. gov/dads. [16] S. Skiena. The Algorithm Design Manual. Springer Verlag, 1997. Gutes Buch mit vielen Algorithmen für den Praktiker. 5 1 Algorithmen auf Graphen – Ergänzung 6 Die Union-Find-Datenstruktur Eine endliche Menge S sei in die disjunkten Klassen Xi partitioniert: S = X0 ∪ X1 ∪ X2 ∪ . . . ∪ Xk mit Xi ∩ Xj = Ø ∀i, j ∈ {0, 1, . . . , k}, i 6= j. Zu jeder Klasse Xi wird ein Repräsentant ri ∈ Xi ausgewählt. Die zugehörige Union-Find-Struktur unterstützt die folgenden Operationen effizient: Init(S): Initialisiert die Struktur und bildet für jedes x ∈ S eine eigene Klasse mit x als Repräsentant. U nion(r, s): Vereinigt die beiden Klassen, die zu den beiden Repräsentanten r und s gehören, und bestimmt r zum neuen Repräsentanten der neuen Klasse. F ind(x): Bestimmt zu x ∈ S den eindeutigen Repräsentanten, zu dessen Klasse x gehört. 7 Implementierung Eine einfache Implementierung speichert die Zugehörigkeiten zwischen den Elementen aus S und den Repräsentanten ri in einer Liste. Für kürzere Laufzeiten werden jedoch in der Praxis Mengen von Bäumen verwendet. Dabei werden die Repräsentanten in den Wurzeln der Bäume gespeichert, die anderen Elemente der jeweiligen Klasse in den Knoten darunter. U nion(r, s): Hängt die Wurzel des Baumes von s als neues Kind unter die Wurzel des Baumes von r. F ind(x): Wandert vom Knoten x aus den Pfad innerhalb des Baumes nach oben bis zur Wurzel und gibt diese als Ergebnis zurück. 8 Heuristiken Um die Operationen Find und Union zu beschleunigen, gibt es die zwei Heuristiken Union-By-Size und Pfadkompression. 9 Union-By-Size Bei der Operation U nion(r, s) wird der Baum, der kleiner ist, unter den größeren Baum gehängt. Damit verhindert man, dass einzelne Teilbäume zu Listen entarten können wie bei der einfachen Implementierung (r wird in jedem Fall Wurzel des neuen Teilbaums). Die Tiefe eines Teilbaums T kann damit nicht größer als log |T | werden. Mit dieser Heuristik verringert sich die Worst-Case-Laufzeit von F ind von O(n) auf O(log n). Für eine effiziente Implementierung führt man bei jeder Wurzel die Anzahl der Elemente im Teilbaum mit. 10 Pfadkompression Um spätere F ind(x) Suchvorgänge zu beschleunigen, versucht man die Wege vom besuchten Knoten zur zugehörigen Wurzel zu verkürzen. 1. maximale Verkürzung (Wegkompression) Nach dem Ausführen von F ind(x) werden alle Knoten auf dem Pfad von x zur Wurzel direkt unter die Wurzel gesetzt. Dieses Verfahren bringt im Vergleich zu den beiden folgenden den größten Kostenvorteil für nachfolgende Aufrufe von F ind für einen Knoten im gleichen Teilbaum. Zwar muss dabei jeder Knoten auf dem Pfad zwischen Wurzel und x zweimal betrachtet werden, für die Laufzeit-Komplexität ist das jedoch unerheblich. 2. Aufteilungsmethode (splitting) Während des Durchlaufes lässt man jeden Knoten auf seinen bisherigen Großvater zeigen (falls vorhanden); damit wird ein durchlaufender Pfad in zwei der halben Länge zerlegt. 3. Halbierungsmethode (halving) 11 Während des Durchlaufes lässt man jeden zweiten Knoten auf seinen bisherigen Großvater zeigen. Diese Methoden haben beide dieselben amortisierten Kosten wie die erste Kompressionsmethode (Knoten unter die Wurzel schreiben). Alle Kompressionsmethoden beschleunigen zukünftige F ind(x)-Operationen. 12 Laufzeiten Union-Find-Datenstrukturen ermöglichen die Ausführung der obigen Operationen mit den folgenden Zeitkomplexitäten (n = |S|): Implementierung mit einer Liste L, worst-case: F ind: O(n), U nion: O(1) Implementierung mit Bäumen: • ohne Heuristiken: F ind: O(n), U nion: O(1) • mit Union-By-Size, worst-case: F ind: O(log(n)), U nion: O(1) • mit Union-By-Size, Pfadkompression, worst-case: F ind: O(log(n)), U nion: O(1) • mit Union-By-Size, Pfadkompression, Folge von f F ind- und u U nionOperationen (amortisiert): O (u + (n + f ) · (log∗(n))) 13 Test auf Zyklus Zur effizienten Durchführung des Tests auf einen Zyklus baut man die Mengen Xi so auf, dass zwei Knoten u und v eines Graphen genau dann in der selben Menge liegen, wenn es einen Weg von u nach v gibt. Bevor man nun eine Kante k = (u, v) zu einem Graphen hinzufügt, testet man, ob u und v bereits durch den bisherigen Graphen verbunden sind (also ob F ind(u) = F ind(v) ist). Ist dies der Fall, so würde man durch hinzufügen von k einen Zyklus erzeugen. Man kann k also nicht hinzufügen. Ist dagegen F ind(u) 6= F ind(v), so kann man k hinzufügen und man vereinigt die beiden, durch u und v identifizierten Mengen, zu einer neuen Menge U nion(u, v). Das folgende Beispiel illustriert die Vorgehensweise: 14 Anwendung von Find Union zur Zyklendetektion Graph Partition 1 2 3 4 5 6 7 8 9 4 2 1 3 5 6 7 9 8 15