Universität Konstanz Fachbereich Informatik und Informationswissenschaft Seminar: Zeichnen von Graphen Simple and Efficient Bilayer Cross Count Ausarbeitung zum gleichnamigen Artikel von Barth, Jünger und Mutzel Proceedings of the 10th International Symposium on Graph Drawing (GD’2002) Lecture Notes in Computer Science 2528, Springer-Verlag, pp. 130 - 141, 2002 Prof. Dr. Ulrik Brandes von Matthias Broghammer [email protected] Version vom 3. Mai 2005 Inhaltsverzeichnis 1 Einleitung 1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Anwendungsbeispiel: Sugiyama-Layouter . . . . . . . . . . . . . . . . . . . . 1 1 1 2 Definitionen 2.1 Grundlegendes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Inversionszahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 3 Entwicklung eines effizienten Algorithmus 3.1 Naiver Ansatz . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Geometrischer Ansatz . . . . . . . . . . . . . . . . . . . 3.3 Ansatz mittels Sortierverfahren . . . . . . . . . . . . . . 3.3.1 MergeSort . . . . . . . . . . . . . . . . . . . . . . 3.3.2 InsertionSort . . . . . . . . . . . . . . . . . . . . 3.4 Der effiziente und einfache O(|E| log |Vsmall |) Algorithmus 4 4 5 5 5 5 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Implementation 10 5 Empirische Laufzeitanalyse 5.1 Testumgebung . . . . . . . 5.2 Dünne Graphen . . . . . . 5.3 Dünne große Graphen . . 5.4 Dichte Graphen . . . . . . 5.5 AT&T-Graphen . . . . . . 5.6 Zusammenfassung . . . . . 10 13 13 14 14 15 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Einleitung Motivation Die Autoren Barth, Jünger und Mutzel beschäftigen sich im untersuchten Artikel [WB02] mit dem Problem, die Zahl der Kantenkreuzungen bei einem bipartiten Graphen zu bestimmen, wenn dieser auf folgende Weise gezeichnet wird: Abbildung 1: Schematische Darstellung einer Zwei-Lagen-Zeichnung (Bilayer Drawing, BLD) Die Knoten der beiden Mengen der Bipartition werden jeweils auf einer Strecke so verteilt, dass sich keine Knoten überlappen. Die beiden Strecken werden dabei parallel untereinander ausgerichtet und die Kanten ebenfalls als gerade Linien gezeichnet. Um einen Graphen oder ein Netzwerk übersichtlich darzustellen, ist es in den meisten Fällen unabdingbar, die Zahl solcher Kreuzungen möglichst gering zu halten. Layout-Algorithmen versuchen dabei, eine Anordnung der Knoten des Graphen zu finden, sodass die Kantenkreuzungszahl möglichst gering ist. Als klassisches Anwendungsbeispiel für einen solchen Algorithmus soll im Folgenden kurz die Vorghensweise des Sugiyama-Layouters vorgestellt werden. 1.2 Anwendungsbeispiel: Sugiyama-Layouter Der Sugiyama-Layout-Algorithmus [KS81] läßt sich in 3 Phasen unterteilen: Erste Phase: Die Knoten des Graphen werden m parallelen Lagen zugeordnet, sodass alle Kanten jeweils zwei Knoten auf unterschiedlichen Lagen verbinden. Dabei wird für solche Kanten, die zwischen zwei nicht-benachbarten Lagen verlaufen, für jede überquerte Lage ein künstlicher Knoten eingefügt. Zweite Phase: Hierbei werden die Anordnungnungen der Knoten auf den einzelnen Lagen bestimmt, um eine möglichst geringe Kantenkreuzungszahl zu erhalten. Dritte Phase: Aus der endgültigen Anordnung wird eine geometrische Repräsentation bestimmt. Dabei tritt das vorgestellte Problem in der zweiten Phase auf: Der Algorithmus betrachtet typischerweise jeweils zwei benachbarte Lagen (Lf ixed , Lf ree ) und versucht, aus einer anfänglichen Permutation der Knoten mittels Heuristik eine neue zu bestimmen, bei der die Kantenkreuzungszahl geringer ist. Dabei verändert er jeweils nur die Knoten der Lage Lf ree , die Permutation der anderen Lage, Lf ixed , wird dabei nicht verändert. Der Algorithmus durchläuft 1 dabei solange die Lagen von unten nach oben und umgekehrt, bis keine Verbesserung mehr erreicht wird. Das Zweilagen-Kreuzungs-Minimierungsproblem ist N P-hart [PE94]. Es gibt jedoch gute Heuristiken und es ist möglich, dieses Problem für Graphen mit bis zu 60 Knoten pro Lage sehr schnell zu lösen [MJ97]. Das Zweilagen-Kantenkreuzungszahlproblem, also die Stelle im Algorithmus, bei welcher die Zahl der Kantenkreuzungen zwischen zwei benachbarten Lagen gezählt wird, kann unter Umständen den Flaschenhals der Gesamtlaufzeit des Sugiyama-Layout-Algorithmus darstellen [CW99]. Es ist also von Interesse, dieses Problem effizient zu lösen. Im Folgenden wird dazu ein Algorithmus entwickelt, der nicht nur in vielen Anwendungsfällen effizienter als die bisher bekannten ist, sondern dazu noch sehr leicht zu implementieren ist. Abbildung 2: Eine Graphandarstellung nach Sugiyama-Layout (mit virtuellen Knoten) 2 2.1 Definitionen Grundlegendes Sei G = (N, S, E) ein bipartiter Graph mit disjunkten Knotenmengen N und S. N bezeichne dabei die nördliche, und S die südliche Knotenmenge der Bipartition. Zusätzlich gelte für jede Kanten e = (u, v) ∈ E, dass entweder u ∈ N, v ∈ S oder u ∈ S, v ∈ N gilt. Das bedeutet, dass alle Kanten ausschließlich zwischen N und S verlaufen. Desweiteren seien LN , LS ∈ R2 zwei diskunkte parallele Strecken, eine nördliche und eine südliche. 2 Eine Zweilagen-Zeichnung BLD(G) (bilayer drawing) weist allen Knoten ni ∈ N = {n0 , n1 , . . . np−1 } disjunkte Punkte P (ni ) auf LN , und allen Knoten sj ∈ S = {s0 , s1 , . . . , sq−1 } disjunkte Punkte P (Sj ) auf LS zu. Den Kanten ek = (ni , sj ) ∈ E = {e0 , e1 , . . . , er−1 } werden Strecken mit Endpunkten P (ni ) und P (sj ) zugewiesen: Abbildung 3: Darstellung eines Bilayer Drawings (BLD) Die Zweilagen-Kantenkreuzungszahl BCC (bilayer cross count) ist dabei die Anzahl von paarweisen, inneren Schnittpunkten der zu den Kanten zugehörigen Strecken. Unser Beispiel aus Abbildung 3 hat also BCC(BLD(G)) = 12. Offensichtlich ist diese Zahl einzig und allein von den relativen Positionen der Knotenpunkte auf LN und LS abhängig, nicht von ihrer exakten Position. Damit ist BCC(BLD(G)) nur von den Knotenfolgen πN von N und πS von S abhängig. Wir wollen also ausgehend von gegebenem πN und πS die Zweilagen-Kantenkreuzungszahl BCC(πN , πS ) effizient berechnen. Dazu nehmen wir vereinfachend und o.B.d.A. an, dass es keine isolierten Knoten gibt und dass q ≤ p, d.h. |S| ≤ |N | gilt. 2.2 Inversionszahl Wir werden sehen, dass zwischen der Inversionszahl und der Kantenkreuzungszahl ein enger Zusammenhang besteht, den wir ausnutzen werden. In einer Folge π = ha0 , a1 , . . . , at−1 i von paarweise vergleichbaren Elementen ai , wird ein Paar (ai , aj ) eine Inversion genannt, wenn gilt: i<j ∧ 3 ai > a j Die Inversionszahl IN V (π) = |{(ai , aj ) | i < j ∧ ai > aj }| ist eine bekannte Maßzahl für den Sortierungsgrad der Folge π. In einem Zweilagen-Graphen mit Nordlagen-Folge πN = hn0 , n1 , . . . , np−1 i und SüdlagenFolge πS = hs0 , s1 , . . . , sq−1 i sei πE = he0 , e1 , . . . , er−1 i lexikographisch sortiert, so dass gilt: ek = (nik , sjk ) < (nil , sjl ) = el ⇐⇒ ik < il oder ik = il ∧ jk < jl Die Kanten in Abbildung 3 sind bereits lexikographisch sortiert. Sei π = hj0 , j1 , . . . , jr−1 i die Folge der Positionen der südlichen Endknoten der Kanten aus E. Dann ergibt sich für unser Beispiel: π = h0, 1, 2, 0, 3, 4, 0, 2, 3, 2, 4i Jede Inversion in π entspricht dabei genau einer paarweisen Kantenkreuzung in der ZweilagenZeichnung BLD(G). 3 Entwicklung eines effizienten Algorithmus Der im Artikel vorgestellte Algorithmus mit einer Laufzeit in O(|E| log |Vsmall |) und Speicherbedarf in O(|E|) stellt derzeit nicht nur theoretisch die effizienteste Möglichkeit dar, die Kantenkreuzungszahl zu bestimmen, sondern ist auch in fast jeder Situation, also abhängig vom Aufbau des Graphen, schneller als die bisherigen Algorithmen zur Lösung dieses Problems. Diese verschiedenen Ansätze sollen im Folgenden kurz erwähnt werden und schließlich die Entwicklung des Algorithmus aufgezeigt werden. 3.1 Naiver Ansatz Eine einfache und naive Methode, die Kantenkreuzungszahl zu bestimmen, ist das Testen aller Paare von Kanten, ob sich diese jeweils schneiden. Dabei vergleicht man einfach ihre Endknoten-Positionen in LN und LS . Dies führt offensichtlich zu einem Algorithmus mit Laufzeit in O(|E|2 ). Dieser Algorithmus bestimmt nicht nur die Zahl der Kreuzungen, sondern die Kreuzungen selbst, was für unser gegebenens Problem nicht notwendig ist. Da die maximal mögliche Anzahl an Kreuzungen in Θ(|E|2 ) liegt, kann es hierfür keinen asymptotisch besseren Algorithmus geben. Daher sollte man sich lediglich mit dem Problem des Zählens von Kreuzungen beschäftigen, nicht mit der Bestimmung der Kreuzungen selbst. 4 3.2 Geometrischer Ansatz Das Zweilagen-Kantenkreuzungsproblem kann als Spezialfall eines grundlegenden geometrischen Problems angesehen werden, das Zählen von paarweisen Kreuzungen einer Menge von Strecken in der Ebene. Sei C die Menge der paarweisen Kreuzungen. Der schnellste bekannte Algorithmus, der alle Kreuzungen bestimmt, ist von Chazelle und Edelsbrunner [BC92] und liegt mit seiner Laufzeit in O(|E| log |E| + |C|) und der Speicherbedarf in O(|E| + |C|). Der schnellste bekannte Algorithmus für das Zählen der Kreuzungen ist von Chazelle [Cha86] und liegt in O(|E|1.695 ) bei einem Speicherbedarf in O(E). Ein anderer geometrischer Ansatz ist von Sander [San96] und basiert auf der Sweep-Line Methode. Die Laufzeit dieses Algorithmus liegt in O(|E| + |C|) bei einem Speicherbedarf in O(E). Dieser Ansatz wurde von Waddle und Malhotra [BC92] noch verbessert, was zu einer Laufzeit in O(|E| log |V |) mit V = N ∪ S und wiederum einem Speicherbedarf in O(|E|) führte. Dieser Algorithmus wird als Durchbruch angesehen, da nicht nur die theoretische, sondern auch die praktische Laufzeit durch diesen Algorithmus erheblich verbessert wurde. Die Autoren Barth, Jünger und Mutzel haben ausführliche Laufzeittests durchgeführt, auf die in Kapitel 5 näher eingegangen wird. 3.3 Ansatz mittels Sortierverfahren Wir erinnern uns, dass die Zahl der Kantenkreuzungen auch durch das Zählen von Inversionen bestimmt werden kann: BCC(πN , πS ) = IN V (π), wobei π die Folge der Positionen der Endknoten aller Kanten in der südlichen Lage war. Es ist bekannt, dass die Zahl dieser Inversionen in O(|E| log |E|) mit Speicherbedarf in O(|E|) bestimmt werden kann. 3.3.1 MergeSort Cormen, Leiserson und Rivest [THC90] schlagen beispielsweise eine Modifikation des MergeSortAlgorithmus vor. Die Berechnung der Folge π, also die lexikographische Sortierung der Kanten, liegt durch Verwendung von RadixSort sowohl von der Laufzeit als auch dem Speicherbedarf in O(|E|), was zu einer Gesamtlaufzeit in O(|E| log RUN(π)) und Speicherbedarf in O(|E|) führt. Hierbei ist RUN(π) die Zahl der Durchläufe, in unserem Fall die Anzahl der sortierten Teilfolgen in π. 3.3.2 InsertionSort Mittels InsertionSort-Algorithmus kann die Inversionszahl und damit die Anzahl der Kantenkreuzungen mit Laufzeit in O(|E| + IN V (π)) und Speicherbedarf in O(|E|) bestimmt werden, was zu einer Laufzeit in O(|E| + |C|) und Speicherbedarf in O(|E|) führt. 5 Aus diesem Ansatz wird nun der einfache und effiziente Algorithmus entwickelt. Die Funktionsweise des InsertionSort-Algorithmus soll an dem Beispiel-Graphen aus Abbildung 3 gezeigt werden. Der Algorithmus läßt sich dabei in 3 Teile gliedern: 1.) Sortiere die Kanten lexikographisch bzgl. πN und πS mittels RadixSort. Dieser Schritt liegt bekanntlich in O(|E|) und wurde in unserem Beispiel (Abbildung 3) bereits durchgeführt. 2.) Um die Folge π zu erhalten, schreibe die Südknoten-Positionen in Sortierreihenfolge der Kanten in ein Array. Im Beispiel erhalten wir hierbei h0, 1, 2, 0, 3, 4, 0, 2, 3, 2, 4i. 3.) Führe den InsertionSort-Algorithmus auf dem Array durch und bestimme die Kantenkreuzungszahl, indem aufaddiert wird, um wieviele Positionen ein Element jeweils bewegt wird. Zum besseren Verständnis wurden in Abbildung 4 auch noch die Knoten aus N und die Kanten eingezeichnet, was für diesen Schritt des Algorithmus nicht nötig ist. Als Lösung erhält man eine Kantenkreuzungszahl von 2 + 4 + 2 + 1 + 3 = 12. Die Korrektheit des Algorithmus folgt aus der Invariante: Wird ein Element bewegt, so stehen in der aktuellen Folge die höher indizierten Elemente direkt vor diesem. Betrachtet man den ersten Schritt in Abbildung 4, so sieht man den Zustand der Folge vor der ersten Sortierung. Die erste Inversion ist an 3. und 4. Stelle zu finden, da gilt: 3 < 4 aber 2 > 0. Um diese Inversion aufzulösen, wird das vierte Element, s0 , zwei Positionen nach vorne bewegt. Wir wissen damit, dass durch die relative Ordnung nun genau zwei Kreuzungen verursacht wurden. Führt man dies für alle Inversionen aus und man folglich am Ende die sortierte, d.h. ursprüngliche Folge der Südknoten-Positionen erhält, so wurden alle Inversionen und damit auch alle Kreuzungen gefunden und zur Gesamtkreuzungszahl aufaddiert. Untersucht man nun die Laufzeit diesesAlgorithmus, so weiß man bereits, dass InsertionSort |E| in O(|E| + |C|) liegt. Da es maximal 2 Inversionen geben kann, liegt der beschriebene Algorithmus in O(|E|2 ). Diese Laufzeit kann aber durch Verwendung eines Akkumulator-Baumes (accumulator tree) erheblich verbessert werden, wodurch wir den gewünschten einfachen und effizienten Algorithmus mit Laufzeit in O(|E| log |Vsmall |) erhalten. 6 Abbildung 4: Zählen der Kantenkreuzungen mittels InsertionSort 3.4 Der effiziente und einfache O(|E| log |Vsmall |) Algorithmus Sei T ein Akkumulatorbaum, d.h. ein balancierter Binärerbaum mit 2c Blättern. Die ersten |S| Blätter seien dabei den Positionen der Knoten der südlichen Lage zugeordnet und sei c ∈ N wie folgt definiert: 2c−1 < q = |S| ≤ 2c . Für unser Beispiel (Abbildung 3) ergibt sich damit der Akkumulatorbaum in Abbildung 5. Diesen Akkumulatorbaum speichert man in einem Array mit 2c+1 − 1 Einträgen. Wie bei der Repräsentation von Binärbäumen ist die Wurzel an Position 0 zu finden, und jeder Knoten an Position i hat seinen Vorgänger an Position b i−1 c. Alle Array-Einträge werden anfänglich 2 7 Abbildung 5: Akkumulatorbaum für unser Beispiel aus Abbildung 3 mit 0 initialisiert. Zusätzlich wird noch die aktuelle Kantenkreuzungszahl aufsummiert, welche auch mit 0 initialisiert wird. Der Algorithmus speichert in den Blättern des Akkumulatorbaumes die Zahl der zugeordneten südlichen Endknoten und in jedem inneren Knoten die Summe der Anzahlen der Kinderknoten. Diese Summen werden durch Abarbeiten der südlichen Endknoten in der durch π gegebenen Reihenfolge gebildet: Für jede solche Position startet man an dem zugeordneten Blatt des Baumes und durchläuft den Baum bis hoch zur Wurzel und erhöht den Eintrag in jedem besuchten Knoten (einschließlich der Wurzel) um eins. Wird dabei ein linker Kindknoten besucht (ungerade Knotenposition), addiert man den Eintrag im rechten Nachbar von diesem zur Kantenkreuzungszahl dazu. Dies geschieht immer genau dann, wenn eine Kante zu einem weiter links liegenden südlichen Endknoten eingefügt wird und dabei alle die Kanten kreuzt, die bereits eingefügt wurden und mit Knoten rechts von dem aktuellen Süd-Knoten liegen. Dieser wichtige Sachverhalt wurde bereits in Abbildung 4 verdeutlicht. In Abbildung 6 sind die aktuellen Stände im Akkumulatorbaum nach Abarbeitung der jeweils ersten vier Positionen in π dargestellt. Bei der Abarbeitung der vierten Position wird die Kantenkreuzungszahl bei Passieren der Knoten 3 und 1 jeweils um eins erhöht. In Abbildung 7 ist der Endstand zu sehen, die Kantenkreuzungszahl erhöht sich dabei beim Abarbeiten der Positionen 4, 7, 8, 9, 10 und beträgt dabei entsprechend 2, 6, 8, 9 und schließlich 12. Die Korrektheit dieses Algorithmus ist wie bei der obigen Variante gegeben. Wenn wir o.B.d.A. annehmen, dass |S| ≤ |N |, also z.B. |Vsmall | = |S|, ergibt sich zusammen mit der Tiefe des Akkumulatorbaumes eine Laufzeit in O(|E| log |Vsmall |), da wir insgesamt |E| Kanten einfügen und die Höhe des Akkumulatorbaumes durch log |Vsmall | beschränkt ist. 8 Abbildung 6: Akkumulatorbaum nach Abarbeitung der ersten vier Position in π 9 Abbildung 7: Akkumulatorbaum nach Abarbeitung aller Positionen in π 4 Implementation Der beschriebene Algorithmus läßt sich sehr einfach, schnell und damit übersichtlich implementieren. Abbildung 8 zeigt den Ausschnitt des Codes (Java), in dem der Baum angelegt wird, Abbildung 9 zeigt den Auschnitt des Codes, indem die Positionen in π durchlaufen und die Kantenkreuzungszahl aufsummiert wird. Eine weitere Besonderheit in der Implementierung stellt die Sortierung der Kanten dar. Um die gewünschte Laufzeit zu erreichen, wird von den Autoren vorausgesetzt, dass diese Sortierung in Zeit O(|E|) zu bewerkstelligen ist. Diese Laufzeit kann z.B. durch das Sortierverfahren RadixSort erreicht werden, dessen Implementation auch recht einfach realisiert werden kann, wie Abbildung 10 zeigt. 5 Empirische Laufzeitanalyse Um nun die Laufzeit des vorgestellten Algorithmus unter praktischen Gesichtspunkten zu analysieren und mit den populärsten und bereits bekannten Algorithmen für das Zählen von Kantenkreuzungen in einem Zweilagengraphen zu vergleichen, wurden von den Autoren verschiedene empirische Laufzeituntersuchungen unternommen. Dazu wurden folgende Algorithmen in C implementiert: 10 Abbildung 8: Java Code Ausschnitt der Initialisierung des Akkumulatorbaumes Abbildung 9: Java Code Ausschnitt der Abarbeitung der südlichen Endknoten-Positionen 11 Abbildung 10: Java Code Ausschnitt der Funktion RadixSort 12 Abkürzung SAN WAM MER INS BJM Algorithmus Sander Waddle und Malhotra MergeSort-Variante InsertionSort-Variante Einfacher Algorithmus Laufzeit-Schranke O(|E| + |C|) O(|E| log |V |) O(|E| log RUN(π)) O(|E| + |C|) O(|E| log |Vsmall |) Quelle [San96] [BC92] Kapitel 3.3.1 Kapitel 3.3.2 Kapitel 3.4 Desweiteren wurden die Algorithmen pro Test jeweils zweimal untersucht: 1.) Auf zufällig erzeugten Zwei-Lagen-Graphen, im Folgenden durch das Kürzel RAN gekennzeichnet, und 2.) auf bereits vor-optimierten Zwei-Lagen-Graphen mittels Median-Heuristik [PE94], im Folgenden durch MED gekennzeichnet. Bei der Implementation wurde darauf geachtet, dass alle Algorithmen so gut und schnell wie möglich implementiert wurden und dass alle über die selbe Schnittstelle / Parameter verfügten: • Zwei Integer-Zahlen, welche die Größe der Nord- bzw. Süd-Schicht angeben (p, q), • Eine Integer-Zahl für die Anzahl der Kanten (r), • Zwei Integer-Arrays (der Größe r), welche jeweils die Position des nördlichen bzw. südlichen Endknoten in der entsprechenden Permutation πN bzw. πS (Nord bzw. Süd) darstellen. Jeder Datenpunkt in den folgenden Diagrammen entspricht einem Mittelwert von 100 Durchläufen auf der gleichen Probleminstanz. 5.1 Testumgebung Alle Laufzeittests wurden von den Autoren auf der selben Hardware ausgeführt (Sony Vaio PCG-R600 Notebook mit Intel Pentium III Prozessor, 850 MHz und 256 MB Hauptspeicher). Zudem wurden alle Algorithmen mit dem selben Compiler (GNU gcc, Optimierungsoption 03 ) compiliert und alle Zufallszahlen von der Funktion gb unif rand aus der Stanford GraphBase-Bibliothek [Knu93] generiert. 5.2 Dünne Graphen Im ersten Laufzeittest wurden dünne Graphen von 1’000 bis 30’000 Knoten pro Lage und 2’000 bis 6’000 zufällig eingefügten Kanten als Eingabe verwendet. Die wesentlichen Ergebnisse dieses Tests (Abbildung 11) sind: • SAN und INS schneiden bei großen Problem-Instanzen sehr schlecht ab, während alle anderen in ihren Laufzeiten deutlich besser sind und nah zusammen liegen. • Außer bei SAN und INS gibt es zwischen den Laufzeiten bei Zufallsgraphen RAN und vor-optimierten Graphen MED lediglich einen geringen Unterschied. 13 Abbildung 11: Laufzeiten für dünne Graphen 5.3 Dünne große Graphen Dieses Verhalten zeigt sich auch, wenn man die Graphen entsprechend vergrößert. Dies ist in Abbildung 12 zu sehen. Die Knotenzahl wurde schrittweise auf 500’000 pro Lage und 1’000’000 zufällig eingefügte Kanten erhöht. Die beste Laufzeit wird dabei ab einer Knotenzahl von ca. 50’000 pro Lage von BJM erreicht. 5.4 Dichte Graphen Im dritten Laufzeittest (Abbildung 13) wurde untersucht, wie sich die Laufzeiten verhalten, wenn bei einer festen Knotenzahl von 1’000 pro Lage die Zahl der zufällig eingefügten Kanten von 1’000 schrittweise auf 100’000 erhöht wird. Ergebnisse dieses Tests: • Wieder sind die Laufzeiten von SAN und INS gegenüber den anderen sehr hoch, • Bis ca. 30’000 Kanten ist BJM der schnellste Algorithmus, ab dieser Kantenzahl ist WAM etwas besser. 14 Abbildung 12: Laufzeiten für große dünne Graphen 5.5 AT&T-Graphen Als letzte Testserie wurden noch einige Realwelt-Graphen aus der Graphen-Sammlung von AT&T [Krü] verwendet. Dabei wurden jeweils mittels der Longest Path- und CoffmanGraham-Methode die Lagen-Paare extrahiert und als Eingabe für die Algorithmen verwendet. Methode Longest Path Coffman-Graham # Nordknoten 1 bis 6’556 1 bis 3’278 # Südknoten 1 bis 5’775 1 bis 3’278 # Kreuzungen 0 bis 10’155’835 0 bis 2’760’466 # Kreuzungen MED 0 bis 780’017 0 bis 2’2872 Wie man in Abbildung 14 sehen kann, führt der BJM-Algorithmus bei den Originaldaten, während INS und SAN deutlich länger brauchen. Werden die Graphen aber mittels MedianHeuristik vor-optimiert, so schneiden INS und MER sehr gut ab. Dies ist auf die geringe Kreuzungszahl zurückzuführen, von dessen Größe die Laufzeit dieser Algorithmen abhängt. 5.6 Zusammenfassung Wählt man für das Zählen der Kantenkreuzungen den BJM, MER oder WAM-Algorithmus, so erhält man stets gute Laufzeit-Resultate. Die Laufzeit ließe sich noch verbessern, wenn man einen Hybrid-Algorithmus entwickelt, der entsprechend charakteristischer Graphenmerkmale die optimale Methode wählt. 15 Abbildung 13: Laufzeiten für dichte Graphen Dabei sollte beachtet werden, dass die Berechnung einer Median-optimierten Knotenfolge laufzeittechnisch sogar minimal teurer als das Berechnen der Kantenkreuzungszahl mit einem der schnellen Algorithmen (BJM, WAM) ist. Im untersuchten Artikel wurde somit eine Methode gezeigt, die Kantenkreuzungszahl in einem Zweilagen-Graphen effizient zu lösen. Zusätzlich dazu ist der Algorithmus mit wenigen Programmzeilen zu realisieren, was ihn zu einer sehr interessanten Alternative zu den bestehenden Algorithmen mit ähnlicher Laufzeit macht. 16 Abbildung 14: Laufzeiten für AT&T Graphen Literatur [BC92] H. Edelsbrunner B. Chazelle. An optimal algorithm for intersecting line segments in the plane. Journal of the ACM, 39:1–54, 1992. [Cha86] B. Chazelle. Reporting and counting segment intersections. Journal of Computer and System Sciences, 32:156–182, 1986. [CW99] A. Malhotra C. Waddle. An e log e line crossing algorithm for levelled graphs. GD 1999, LNCS, 1731:59–70, 1999. [Knu93] E. Knuth. The Stanford GraphBase: A platform for combinatorial computing. Addison-Wesley, Reading, Massachusetts, 1993. [Krü] M. Krüger. http://www.graphdrawing.org, downloadquelle für at&t-graphen. MaxPlanck-Institut für Informatik in Saarbrücken. [KS81] M. Toda K. Sugiyama, S. Tagawa. Methods for visual understanding of hierarchical system structures. IEEE Transactions on Systems, Man, and Cybernetics, 11:109– 125, 1981. [MJ97] P. Mutzel M. Jünger. 2-layer straight line crossing minimization: performance of exact and heuristic algorithms. Journal of Graph Algorithms and Applications, 1:1–25, 1997. [PE94] N. Wormwald P. Eades. Edge crossings in drawings of bipartite graphs. Algorithmica, 11:379–403, 1994. [San96] G. Sander. Visualisierungstechniken für den Compilerbau. Pirrot Verlag & Druck, Saarbrücken, 1996. 17 [THC90] R.L. Rivest T. H. Cormen, C. E. Leiserson. Introduction to algorithms. MIT Press, Cambridge, 1990. [WB02] P. Mutzel W. Barth, M. Jünger. Simple and efficient bilayer cross counting. GD 2002, LNCS, 2528:130–141, 2002. 18