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