05.11.2012 Aufrufe

Seminar Zeichnen von Graphen - Fachbereich Informatik ...

Seminar Zeichnen von Graphen - Fachbereich Informatik ...

Seminar Zeichnen von Graphen - Fachbereich Informatik ...

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!