Wasser für die Wüste Praktischer Umgang mit Steiner Bäumen

Werbung
Wasser für die Wüste
Praktischer Umgang mit Steiner Bäumen
René Trumpp
Matrikel Nr. 021980
Betreuer: Prof. Dr. habil. Thomas Thierauf
Hochschule Aalen
Studiengang Informatik
Datum: 15. März 2006
Inhaltsverzeichnis
1 Einleitung
1.1 Wasser für die Wüste . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Grundbegriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Ziel und Abgrenzung . . . . . . . . . . . . . . . . . . . . . . . . .
3
3
5
6
2 Minimal aufspannende Bäume
2.1 Problembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Ein erster Lösungsansatz . . . . . . . . . . . . . . . . . . . . . . .
2.3 Ein “gieriger“ Lösungsansatz . . . . . . . . . . . . . . . . . . . . .
7
7
8
9
3 Steiner Problem in Graphen
3.1 Problembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Lösungsansatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
11
12
4 Rechtwinkliges Steiner Problem
4.1 Problembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Lösungsansatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
14
15
5 Geographisches Steiner Problem
5.1 Problembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Lösungsansatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
16
16
6 NP-Vollständige Probleme
6.1 Deterministisch oder nicht ? . . . . . . . . . . . .
6.2 Was ist NP ? . . . . . . . . . . . . . . . . . . . .
6.3 Steiner Problem ist ein NP-Vollständiges Problem
6.4 Andere NP-Vollständige Probleme . . . . . . . . .
6.4.1 Problem des Handelsreisenden . . . . . . .
6.4.2 Rucksackproblem . . . . . . . . . . . . . .
.
.
.
.
.
.
17
17
17
18
20
20
20
7 Näherungsfunktionen
7.1 Sonderfälle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1.1 Keine Steinerknoten . . . . . . . . . . . . . . . . . . . . .
7.1.2 Nur zwei Terminale . . . . . . . . . . . . . . . . . . . . . .
22
22
22
22
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
René Trumpp
7.2
7.3
.
.
.
.
.
.
.
.
.
23
23
24
25
26
27
28
28
30
8 Zusammenfassung
8.1 Rückblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
31
9 Anhang
9.1 Implementierung . . . . . . . . . . . . . . .
9.1.1 Klassendiagramm . . . . . . . . . . .
9.1.2 Quelltexte . . . . . . . . . . . . . . .
Graph Klasse . . . . . . . . . . . . .
Node Klasse . . . . . . . . . . . . . .
Minimal Spanning Tree Klasse . . . .
Steiner Baum Trivial Klasse . . . . .
Rechtwinkliger Steiner Graph Klasse
Naeherung Ratio 2 Klasse . . . . . .
Distance Graph Klasse . . . . . . . .
32
32
32
33
33
37
38
39
40
42
44
7.4
7.5
7.6
Qualitätskriterien für Näherungsfunktionen
Näherungsfunktion mit Ratio 2 . . . . . .
7.3.1 Satz . . . . . . . . . . . . . . . . .
7.3.2 Beweis . . . . . . . . . . . . . . . .
7.3.3 Algorithmus . . . . . . . . . . . . .
7.3.4 Beispiel . . . . . . . . . . . . . . .
Näherungsfunktion mit Ratio 1,217 . . . .
Andere Näherungsfunktionen . . . . . . .
Schranke . . . . . . . . . . . . . . . . . . .
Wasser für die Wüste
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Literaturverzeichnis
46
Abbildungsverzeichnis
47
2
1 Einleitung
1.1 Wasser für die Wüste
In einem kleinen Land weit hinter dem Horizont lebte einst ein gütiger und sehr
gerechter König. Sein Name war König Algorimas (von seinen Freunden wurde
er Algo genannt). Algorimas war bei seinem Volk sehr beliebt, denn er war stets
bemüht sein Volk an seinem Reichtum teilhaben zu lassen und so lebten alle in
seinem Land glücklich und zufrieden. Doch eines Tages brach eine große Dürre
über das Land herein. So verwüstete das Land mehr und mehr...
Abbildung 1.1: Das Königreich von König Algorimas
Trotz seines Reichtums konnte der gute König Algorimas nichts gegen das große
Leid in seinem Land tun. In einigen Städten gab es zwar noch Wasserquellen, aber
leider nicht in allen. Aus diesem Grund überlegte sich der König, dass er ein
Leitungssystem bauen lassen musste, welches alle Städte miteinander verbindet
und so Wasser für die Wüste liefert.
Da er aber nicht dumm war, wollte er so wenig Leitungen wie möglich verlegen
lassen. Denn Leitungen waren teuer und es war harte und schwere Arbeit sie
herzustellen und zu verlegen. Deshalb lies er verlauten, dass derjenige, der ihm
3
René Trumpp
Wasser für die Wüste
das kürzeste Leitungssystem plant, seine wunderschöne Tochter heiraten darf und
sein Nachfolger werden würde...
Das könnte der Beginn eines schönen Märchens sein, ist es aber leider nicht.
Mit dieser kleinen Geschichte möchte ich sie zu einem ersten Nachdenken über
folgende Problematik bringen:
In einer Ebene hat man verschiedene Punkte und möchte diese miteinander verbinden. Es soll der kürzeste Weg gefunden werden.
So einfach sich diese Problemstellung auch anhört, ich werde ihnen in den folgenden Kapiteln zeigen, dass dieses Problem (und einige Abwandlungen) nicht
effizient lösbar ist. Ich werde mich dabei immer wieder auf unseren König Algorimas beziehen, da sich an diesem Beispiel vieles leichter erklären lässt. Sollten sie
jetzt denken, dass das Beispiel mit dem König völlig überholt ist und es heute keine praktische Relevanz mehr hat, muss ich ihnen leider widersprechen. Natürlich
werden wir heute keinen König mehr finden, der solche Probleme hat (oder eine
schöne Tochter deshalb verheiraten will), jedoch lassen sich folgende Szenarien
auch auf das gleiche Problem zurückführen:
• Wasserversorgung in Städten
• Stromversorgung
• Netzwerke (z.B. Telefon, LAN)
• Leiterbahnen auf Platinen
• ...
Um leichter mit den Problemen arbeiten zu können, müssen wir die reale Welt
in unsere theoretische Welt überführen. Wir müssen das Problem formal beschreiben. In unserem Beispiel ist das aber ganz einfach. Die Landkarten (siehe
Abbildung 1.1) lässt sich in folgenden Graphen umwandeln:
a g
E
f
?
??
??
??
?
e
?b
??
??
??
? c
/
//
//
//
//
//
//
d
Abbildung 1.2: Landkarte als Graph
4
René Trumpp
Wasser für die Wüste
Hierbei stellen die Knoten V die Städte dar und das Gewicht der Kanten E den
Abstand der Städte voneinander. So stellt der Knoten a die Stadt Altonia und
der Knoten b die Stadt Brulania dar. Damit beschreibt das Gewicht der Kante
Eab , die Entfernung von Altonia zu Brulania.
1.2 Grundbegriffe
Um richtig miteinander “reden“ zu können, müssen wir uns vergewissern, dass
wir mit denselben Begriffen auch dasselbe meinen. Denn ein Baum muss nicht
immer mit den Wurzeln im Boden stecken, aber eins nach dem anderen:
Graph Unter einem Graph G versteht man ein Paar (V ,E), wobei V eine endliche Knotenmenge (vertex) und E eine endliche Menge von Kanten (edge)
bezeichnen. Es ist nicht verlangt, dass alle Knotenpaare durch Kanten miteinander verbunden sind.
Kante Eine Kante verbindet genau zwei Knoten. Kanten können gerichtet oder
ungerichtet sein. Man kann das sehr gut mit Einbahnstraßen und normalen
Straßen vergleichen. Bei ungerichteten Kanten gilt: Eab = Eba
a / b
c d
Abbildung 1.3: Gerichtete und ungerichtete Kante
Vollständigkeit In einem vollständigem Graphen G ist jeder Knoten mit jedem
anderen Knoten verbunden.
Gewicht Das Gewicht einer Kante ist eine Markierung der Kante, welche z.B. ihre
Länge, eine Masse oder einen Euro-Betrag repräsentieren kann. Abkürzend
schreibt man statt Gewicht der Kante e einfach λ(e). λ wird dann als Gewichtsfunktion bezeichnet. Aus gewichteten Kanten bestehende Graphen
nennt man gewichtete Graphen. Das Gewicht eines Weges zwischen zwei
a 5
b
Abbildung 1.4: Gewichtete Kante bzw. gewichteter Graph
Knoten (u, v) ist die Summe der Gewichte aller Kanten auf dem Weg von
u nach v. Das Gewicht eines Graphen, ist die Summe der Gewichte aller
Kanten im Graph.
5
René Trumpp
Wasser für die Wüste
Baum Unter einem Baum B versteht man einen ungerichteten Graphen, der alle
Knoten V mit (|V | − 1) Kanten miteinander verbindet. Ist der Baum ein
Teilgraph eines Graphen G mit |VB | = |VG |, so heißt dieser aufspannender
Baum von G.
Blatt Als Blätter werden Knoten in Bäumen bezeichnet, die nur mit einem einzigen weiteren Knoten über eine Kante verbunden sind.
Effizient Ein Algorithmus heißt effizient, wenn er ein vorgegebenes Problem mit
möglichst geringem Ressourcenaufwand (Zeit, Speicher) löst. Wir werden
nur die Laufzeit, also die Zeiteffizienz, der Algorithmen betrachten. Dazu
verwenden wir die Groß-O O Notation (siehe z.B. [Wag03]).
Anzahl Zahl der Elemente in einer Menge (Abkürzung: ] )
1.3 Ziel und Abgrenzung
Es gibt viele Probleme, von denen glaubt man im ersten Moment, dass sie schon
irgendwie (effektiv) lösbar sind. Ich möchte Ihnen in den nächsten Kapiteln einige
Probleme ähnlich dem Einführungsbeispiel vorstellen. Es soll dabei gezeigt werden, wie sich kleine Unterschiede in der Problemstellung auf die Möglichkeit zur
Lösung des Problems auswirken können. Sie werden sehen, dass manche Probleme
schnell und einfach, andere wiederum nicht effizient lösbar sind.
Insbesondere werde ich ihnen das s.g. Steiner Baum Problem vorstellen. Es gehört
zu den NP-Vollständigen Problemen, weshalb auch kein effizienter Algorithmus
zur Lösung dieses Problems existiert. Deshalb werde ich ihnen am Ende noch
Algorithmen zur Näherung der Lösung vorstellen. Sie werden sehen, dass diese
in akzeptabler Zeit vielleicht nicht die perfekte Lösung liefern (aber fast).
6
2 Minimal aufspannende Bäume
2.1 Problembeschreibung
Schon Pythagoras hat 600 v. Chr. herausgefunden, dass der kürzeste Weg zwischen zwei Punkten die Gerade ist. Deshalb dachte König Algorimas (sehr blauäugig
wie wir später sehen werden), dass das auch für mehrere Punkte gelten muss. Er
stellte also die Aufgabe wie folgt:
Das Leitungssystem soll alle Städte verbinden. Außerhalb der Städte ist es nicht
möglich die Leitungen zu verzweigen. D.h. jede beliebige Stadt kann auf direktem
Weg mit jeder anderen Stadt verbunden werden. Es sollen so wenig Leitungen wie
möglich verwendet werden.
a g
//

//

//


//
c

//

jjjj ///
j
j

j
//
//
jjj

/ jjjjjjj
//

?j
//
?
??
f
//
??
//
??
b
e
d
Abbildung 2.1: Beispiel für einen aufspannenden Baum
Übersetzen wir zuerst die Aufgabe von König Algorimas wieder in unsere theoretische Betrachtungsweise. Es ist ein Graph G mit V Knoten gegeben. Die Knoten
sind mit gewichteten Kanten E verbunden. Bei König Algorimas würde immer ein
vollständiger ungerichteter Graph entstehen (man kann ja immer den Abstand
zweier Städte zueinander messen), wir wollen sein Problem aber gleich etwas allgemeiner lösen und setzen keinen vollständigen Graphen voraus (es muss also nicht
jeder Knoten mit jedem anderen verbunden sein). Gesucht ist der Teilgraph G∗ ,
der alle Knoten V miteinander verbindet und dessen Summe der Kantengewichte
minimal ist. Diese Problemstellung ist in der Literatur als “Minimal aufspannender Baum“ bekannt.
7
René Trumpp
Wasser für die Wüste
a ?b
/
 ???
///

??

//
??


//
/ c


//
//



//
//

/ 
//
?
//
g
f ???
//
??
//
??
e
d
Abbildung 2.2: Weiteres Beispiel für einen aufspannenden Baum
2.2 Ein erster Lösungsansatz
Ein Lösungsansatz, der wahrscheinlich jedem zuerst in den Sinn kommt ist der
einfachste:
Wir berechnen jeden möglichen aufspannenden Baum G∗ und suchen
den mit dem kleinsten Gewicht heraus.
Dass dieser verbal beschriebene Algorithmus sicherlich richtig ist und auch immer
funktioniert ist klar, jedoch müssen wir seine Laufzeit betrachten. Dazu sollten
wir zuerst einige Überlegungen anstellen.
Die Anzahl der Kanten hängt in einem vollständigen Graphen von der Anzahl n
der Knoten ab:
]E =
n
X
(i − 1) = 0 + 1 + 2 + · · · + (n − 1) =
i=1
n−1
X
(i) =
i=1
n ∗ (n − 1)
2
Diese Formel kann man sehr schnell herleiten, wenn man sich überlegt, dass bei
jedem neuen Knoten eine neue Kante zu jedem bereits vorhandenem Knoten
hinzukommt.
Ein aufspannender Baum hat immer n − 1 Kanten.
Will man nun aber die Anzahl der Bäume in einem Graphen mit n Knoten herausfinden, wird es schwieriger. In [CoLeRiSt01] wird bewiesen, dass sich die Anzahl
mit Hilfe der s.g. Catalan-Zahlenfolge (1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796,
...) berechnen lässt. So ergibt sich
2n
(2n)!
1
∗
=
]ST =
(n + 1)
n
(n + 1)!n!
Mit diesem Wissen ist unser doch so netter Algorithmus fast wertlos, denn so
viele Bäume möchte man doch nicht vergleichen.
8
René Trumpp
Wasser für die Wüste
2.3 Ein “gieriger“ Lösungsansatz
Doch damit geben wir uns natürlich nicht zufrieden. Es geht nämlich besser. Dazu
müssen wir einfach “gierig“ sein. Schauen sie sich hierzu folgenden Algorithmus
an:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Wähle beliebigen Knoten s ;
S ={ s };
for each v ∈ V \{ s } do {
Distanz ( v )= Kante (s , v );
Vorgänger ( v )= s ;
}
Distanz ( s )=0;
while S6= V do {
Finde u∈V \ S mit Distanz ( u )= min { Distanz ( v )| v∈V \ S };
S = S∪{ u };
for each v∈V \ S do {
if Kante (u , v ) < Distanz ( v ) then {
Distanz ( v )= Kante (u , v );
Vorgänger ( v )= u ;
}
}
}
Dieser Algorithmus ist insofern “gierig“, als dass er immer vom aktuellen Teilbaum aus die kleinste angrenzende Kante auswählt und hinzufügt. Er wählt so
nie eine falsche Kante aus. Diese Art Algorithmen haben in der Literatur die
Bezeichnung Greedy-Algorithmen und können auf viele Problemstellungen angewendet werden (siehe z.B. [Wag03]).
Doch nun wollen wir die Abschätzung der Laufzeit des Algorithmus betrachten:
Die For-Schleife aus Zeile 3 läuft n − 1 mal durch. Die While-Schleife aus Zeile
8 läuft auch n − 1 mal durch, denn bei jedem Durchlauf wird ein Knoten zu S
hinzugefügt. Die Laufzeit der Suche in Zeile 9 hängt stark von der verwendeten
Datenstruktur ab, in welcher wir die Knoten und Kanten speichern. Wir wollen
sie jetzt einfach mit log n abschätzen. Die For-Schleife aus Zeile 11 wird bei jedem
Durchlauf weniger oft durchlaufen, jedoch schätzen wir sie mit n ab. Daraus ergibt
sich:
O(n − 1) + O(n ∗ log n) + O(n ∗ n) = O(n2 )
Trotz dieser sehr groben Abschätzung und des noch verbesserungsfähigen Algorithmus haben wir so eine quadratische Laufzeit erreicht. Eine merkliche Verbesserung zu unserem ersten Ansatz. In der Literatur (siehe [ProSte02]) ist er unter
9
René Trumpp
Wasser für die Wüste
“Prim Algorithmus für aufspannende Bäume“ zu finden (nach seinem Erfinder
Robert C. Prim) und lässt sich bis zu einer Laufzeit von O(e + n ∗ log n) (mit e
= Anzahl der Kanten) verbessern.
Wir werden aber nun sehen, dass so eine Verbesserung leider nicht immer möglich
ist.
10
3 Das Steiner Problem in Graphen
3.1 Problembeschreibung
Als König Algorimas nochmals über seine gestellte Aufgabe nachdachte, fiel ihm
auf, dass er wohl einen Fehler gemacht haben musste. Er überlegte sich an 4
Städten die ganze Situation nochmals durch.
a b
2
c 2
2
d
Abbildung 3.1: Lösung für König Algorimas erste Aufgabe bei 4 Punkten
Wenn die 4 Städte in einem Quadrat angeordnet sind, und jede Stadt zu seiner nächsten Nachbarstadt den Abstand 2 hat, so wäre die kürzeste Lösung ein
Leitungssystem der Länge 2 + 2 + 2 = 6.
a ??
??
??
??
v




c 
b






??
??
??
??
d
Abbildung 3.2: Lösung für 4 Punkte mit zusätzlichem Punkt in der Mitte
König Algorimas überlegte nun weiter und erkannte, dass wenn man in der Mitte
des Quadrats eine zusätzliche Verteilerstation bauen würde, dass die Länge des
benötigten Leitungssystems kleiner werden würde. Die Länge
√ des Leitungssystems
√
wäre dann die doppelte Länge einer Diagonalen, also 2∗( 2∗2) = 4∗ 2 ≈ 5, 66.
Er erkannte, dass durch zusätzliche Verteilerstationen die Länge des Leitungssystems verkürzt werden kann. Er schaute sich die Landkarte seines Reichs an und
zeichnete einige neue Punkte ein, an denen man Verteilerstationen bauen könnte.
Er änderte seine Aufgabenstellung wie folgt:
11
René Trumpp
Wasser für die Wüste
Das Leitungssystem soll alle Städte verbinden. Außerhalb der Städte ist es nur
an den Verteilerstationen möglich die Leitungen zu verzweigen. Es müssen nicht
alle Verteilerstationen genutzt werden.
a g
b
//


//

//


s
t 
//
c

//



// 


/ 

u

?
OOO
OOO
f ???
OOO
??
OOO
??
O
e
d
Abbildung 3.3: Landkarte mit zusätzlichen Verteilerstationen
Bevor wir uns um die genaue Aufgabenstellung widmen, müssen wir zunächst die
neuen Gegebenheiten betrachten. Es ist ein vollständiger Graph G mit V Knoten
gegeben. Die Knoten V sind aber in zwei disjunkte Mengen unterteilt, in die
Terminale () und die s.g. Steinerknoten ( ). Ein Baum B, der alle Terminale
und beliebig viele Steinerknoten miteinander verbindet, heißt Steiner Baum, falls
kein Steinerknoten ein Blatt ist.
Die neue Aufgabenstellung heißt also ganz einfach, finde für einen gegebenen
Graphen G mit V Knoten (Terminale und Steinerknoten) den minimalen Steinerbaum. Das Problem ist NP-Vollständig (siehe Kapitel 6).
3.2 Lösungsansatz
Ohne Steinerknoten wäre das Problem einfach mit dem Algorithmus für aufspannende Bäume zu lösen. Man würde den aufspannenden Baum des Graphen
berechnen und hätte die Lösung. Vielleicht könnte man an diesem Ansatz ja weitermachen. Nehmen wir an, wir hätten einen Graphen G, der nur aus Terminalen
besteht. Fügt man in G nun einen Steinerknoten s1 ein, muss man den minimal
aufspannenden Baum von G und von G ∪ {s1 } berechnen und danach den kleineren auswählen. Soweit funktioniert das ganz gut, aber jetzt kommt das Problem.
Fügen wir jetzt noch einen Steinerknoten s2 hinzu, müssen wir den minimal aufspannenden Baum von G,G ∪ {s1 }, G ∪ {s2 } und von G ∪ {s1 , s2 } berechnen und
danach den kleinsten auswählen. Fügen wir nun einen weiteren Steinerknoten hinzu, müssen wir noch mehr Kombinationen durchprobieren. Außerdem können wir
das Wissen aus vorherigen Untersuchungen nicht verwenden, denn auch wenn ein
Steinerknoten si in einem Schritt nicht im minimalen Steinerbaum enthalten ist,
muss das nicht bedeuten, dass er in allen folgenden Schritte auch nicht enthalten
ist.
12
René Trumpp
Wasser für die Wüste
Mit jedem neuen Steinerknoten verdoppelt sich die Anzahl der zu untersuchenden
Teilgraphen. D.h. bei n Steinerknoten müssen 2n minimal aufspannende Bäume
berechnet und verglichen werden.
13
4 Das rechtwinklige Steiner
Problem
4.1 Problembeschreibung
König Algorimas hatte eingesehen, dass zusätzliche Verteilerstationen die Leitungslänge verkürzen können. Er schaute sich also nochmals seine Landkarte an
und legte ein Gitternetz darüber.
Er stellte seine Aufgabe erneut:
Das Leitungssystem soll alle Städte verbinden. Außerhalb der Städte sind an allen
Gitternetzkreuzungen neue Verteilerstationen möglich. Die Leitungen dürfen nur
entlang der Gitternetzlinien verlaufen.
_ _ _ _
_ _ _ _
_ _ _ _
g
_ _ _ _
_
a__
_ __
_ __ _ __
_ _ _ _ _ _
f _ __ _ __
_ __
_ __
_ __
_ _ _
e
_
_
_
_
_ _b
__
__
__
_ __
_ _ _c
_ __
_ __
_ _ _
_ _ _
_ _ _
_ __
d
_ _
_ _
_ _
_ _
Abbildung 4.1: Eine mögliche Lösung des rechtwinkligen Steinerproblems
Wir wollen nun diese Aufgabenstellung in die Graphentheorie übersetzen. Gegeben ist ein Graph G mit V Knoten. Die Koordinaten von V entsprechen (Vx ,
Vy ). Es dürfen beliebig viele Steinerknoten S mit den Koordinaten (Sx ,Sy ) hinzugefügt werden, wobei gelten muss, ∃v ∈ V , so dass gilt vx = sx oder vy = sy .
Gesucht ist wieder ein minimal aufspannender Baum.
14
René Trumpp
Wasser für die Wüste
4.2 Lösungsansatz
Zuerst muss man sich überlegen, wie viele Steinerknoten in dem Gitternetz möglich
sind. Dazu betrachtet man die Höhe und Breite des Netzes.
_ _ _ _ _ _ _ _ _ _ _ _
_ _ __ _ __ _ __ _ _
_ _ _ _ _ _ _ _ _ _ _ _
Abbildung 4.2: Gitternetz mit Höhe und Breite 3
In einem Netz mit der Höhe h und Breite b gibt es insgesamt h ∗ b Gitternetzkreuzungen. D.h. die maximale Anzahl der Steinerknoten beträgt
(h ∗ b) − ]V
Mit diesem Hintergrundwissen können wir nun die Formel aus Kapitel 3.2 abwandeln. Da die Anzahl der Steinerknoten nun nicht mehr vorgegeben ist, müssen
wir vom Maximum ausgehen.
]ST = 2]S = 2(h∗b)−]V
Die Anzahl der zu untersuchenden minimal aufspannenden Bäume hängt also
nun von der Höhe und Breite des Graphen, sowie der Anzahl der Terminale ab.
15
5 Das geographische Steiner
Problem
5.1 Problembeschreibung
König Algorimas wollte nun noch den letzten Schritt machen und die Aufgabenstellung nochmals erweitern. Er lies alle vorherigen Einschränkungen weg und
stellte seine Aufgabe neu:
Das Leitungssystem soll alle Städte verbinden. Außerhalb der Städte sind an allen
Punkten neue Verteilerstationen möglich.
a b





s
c


 ???





??


?? 


? 

t


?
OOO
g
OOO
f ???
OOO
??
OOO
??
O
e
d
Abbildung 5.1: Landkarte mit beliebigen zusätzlichen Verteilerstationen
5.2 Lösungsansatz
Die exakte Lösung dieses Problems ist nahezu unmöglich. Wir haben ein paar
Punkte in einer Ebene und dürfen jeden beliebigen Punkt einfügen. Wenn wir
wieder an unsere Formel aus Kapitel 3.2 denken, müsste im Exponenten ja
“Unendlich“ stehen.
Aus diesem Grund stellt sich uns jetzt die Frage, muss es denn immer die exakte
Lösung sein? Warum sind wir denn nicht mit einer fast genauso guten Lösung
zufrieden, wenn es uns viel leichter fallen würde, diese zu finden? Deshalb wollen
wir uns nun mit der Näherungen des Steiner Problems befassen.
16
6 NP-Vollständige Probleme
6.1 Deterministisch oder nicht ?
In der theoretischen Informatik haben wir oft mit den verschiedensten Problemstellungen zu tun. Wir gehen strukturiert an das Problem heran und suchen nach
einer Lösung. Dabei haben wir immer eine klare Richtung, verlassen uns nie auf
den Zufall oder auf göttliche Eingebung, denn wie sollte man auch damit rechnen
können, der Computer kann es ja auch nicht. Diese Anschauung bezeichnet man
als deterministisch. Bei dem beliebten Minimum- oder Maximum-Suchproblem
haben wir ein sehr gutes Beispiel: Wir sagen dem Computer, er solle doch alle
möglichen Lösungen anschauen und uns nach dem Vergleich aller, das kleinste
bzw. größte ausgeben.
Es gibt jedoch auch einen anderen Ansatz, warum kann man nicht einfach zum
Computer sagen, wähle das kleinste bzw. größte aus und gib es mir dann auf dem
Bildschirm aus. Wir gehen also davon aus, der Computer wisse bereits vorher
die Lösung und wir müssen ihn nur noch darum bitten, sie uns auszugeben.
Sie werden jetzt verständlicherweise den Kopf schütteln und sich fragen, warum
wir das hier betrachten, denn so etwas ist ja nicht möglich. Es gibt kein Orakel
von Delphi, welches der Computer fragen könnte. Das stimmt. Jedoch ist diese
nichtdeterministische Art der Fragestellung für uns zu einem späteren Zeitpunkt
noch extrem wichtig.
6.2 Was ist NP ?
Wir haben bereits das “Minimal aufspannende Baum“ Problem kennen gelernt.
Im Nachhinein kann man sagen, dass es doch sehr einfach zu lösen war. Doch wenn
wir jetzt wieder auf ein Problem stoßen brauchen wir eine Metrik, ein Lineal, mit
dem wir das neue Problem messen können. Erst dann können wir eine Aussage
darüber treffen, welches einfacher oder schwerer ist. Diese Messung geschieht in
der theoretischen Informatik durch die Einteilung in die s.g. Komplexitätsklassen.
Es gibt davon verschiedene, wobei gleich schwere Probleme in der gleichen Klasse
liegen.
17
René Trumpp
Wasser für die Wüste
Nun stellt sich die Frage, wie wir die Einteilung eines Problems in eine solche Klasse vornehmen. Man hat sich dazu entschlossen, die Probleme nach ihrer Laufzeitkomplexität einzuordnen. In der Klasse P liegen also alle Probleme, die deterministisch in polynomialer Zeit für Eingaben der Länge n eine Lösung finden. Analog
dazu liegen in N P alle Probleme, die nicht-deterministisch in polynomialer Zeit
für Eingaben der Länge n eine Lösung finden.
Eine formale Definition von NP lautet: Ein Problem L ⊆ Σ∗ ist genau dann in
NP, wenn es ein Polynom p(n) und ein Problem L0 ∈ P gibt, so dass gilt:
L = {x ∈ Σ∗ |∃y, |y| ≤ p(|x|) ∧ (x, y) ∈ L0 }
Es gibt noch eine zweite (vielleicht einfachere) Möglichkeit, die Klasse N P zu
beschreiben: ein Algorithmus rät (nicht-deterministisch) eine Lösung für das Problem und prüft danach in polynomialer Zeit nach, ob diese stimmt.
Aus den obigen Definitionen wird klar, dass P ⊆ N P gilt. Ob aber P = N P gilt,
ist noch ungeklärt, wobei im Moment P 6= N P vermutet wird.
Ein Problem ist NP-Vollständig, wenn es in der Komplexitätsklasse NP liegt und
wenn sich jedes andere Problem in NP auf dieses reduzieren lässt.
6.3 Steiner Problem ist ein NP-Vollständiges
Problem
Der Beweis, dass das Steiner Problem ein NP-Vollständiges Problem ist, wird
durch die Reduktion des 3SAT - Problems auf das Steiner Problem geführt.
Das NP-Vollständige 3SAT - Problem stellt sich wie folgt: Gegeben ist eine boolsche Formel F in konjunktiver Normalform bei der jede Klausel maximal 3 Literale enthält. Gibt es eine Variablenbelegung, die F erfüllt ?
Beispiel: F = (x1 ∨ x2 ∨ x3 ) ∧ (x1 ∨ x3 ) ∧ (x2 ∨ x3 ∨ x4 )
Zuerst müssen wir das 3SAT - Problem in ein Steiner Baum Problem transformieren. Dazu konstruieren wir einen Graphen G wie folgt: Wir verbinden zwei
Variablen u, v über einen “Literal-Pfad“ (siehe Abbildung 6.1), dazu stellen wir
allen n Literalen x1 , . . . , xn den negativierten Literal x1 , . . . , xi gegenüber.
Für jede der m Klauseln fügen wir einen neuen Knoten Ci zu G hinzu, wobei
jedes Ci über eine Kante der Länge t = 2n + 1 mit den in Ci vorkommenden
Literalen verbunden ist.
Als Terminale wählen wir K = {u, v} ∪ {C1 , . . . , Cm }. Den Grenzwert B legen
wir auf B = 2n + t ∗ m fest.
18
René Trumpp
Wasser für die Wüste
u
CV1,
l h R M
q
)
G
w
?

%
7
1
"
,
x1
x2 x3
x4
??
??
??



 ??
 ???
 ???
 ???
 ???




??
??  ?? 
?? 

?v
? 
?  ? 

??
??
 ??
??



 ??
 ??
??
 ???

?? 

??
??
?? 

?? 
?
?? 

? 
? 


x1#
x2 8 x3#
x
4
>
D
/
/
~
J
|
N
@
S @V H
H xz
Nv
ZN#
C2
C3
Abbildung 6.1: Transformiertes 3SAT-Problem
Das Steiner Baum Problem lautet nun: Ist die Länge des Steiner Baums in G mit
den Terminalen K kleiner als B.
Mit Hilfe einer Reduktion zeigen wir nun, dass gilt
• 3SAT ist erfüllbar ⇒ Steiner Baum hat Länge ≤ B
• Steiner Baum hat Länge ≤ B ⇒ 3SAT ist erfüllbar
und somit, dass das Steiner Baum Problem NP-Vollständig ist.
Zuerst nehmen wir an, dass das 3SAT - Problem erfüllbar sei. Um einen Steiner
Baum für K zu erzeugen, starten wir mit einem u − v Pfad P , der eine akzeptierende Belegung darstellt. Dabei bedeutet xi ∈ P , dass xi in der Belegung als
“wahr“ verwendet wird. Analog bedeutet xi ∈ P , dass xi in der Belegung als
“falsch“ verwendet wird. Betrachten wir nun für jede Klausel den Knoten Ci ,
so sehen wir, dass wir Ci mit einer Kante der Länge t zu P hinzufügen können.
Dadurch erhalten wir einen Steiner Baum von K mit der Länge 2n + t ∗ m = B.
Um die andere Folgerungsrichtung zu zeigen, nehmen wir zuerst an, T sei ein
Steiner Baum für K mit höchstens der Länge B. Man sieht sehr schnell, dass
für jede Klausel der Knoten Ci mit dem Pfad verbunden sein muss. Nehmen wir
einmal an, es gäbe eine Klausel Ci0 , die mit mehr als einer Kante mit dem Pfad
verbunden wäre. Dann wäre die Länge von T ≥ (m + 1) ∗ t ≥ B, es ist also nicht
19
René Trumpp
Wasser für die Wüste
möglich. Das zeigt, dass u und v nur entlang des “Literal-Pfads“ miteinander
verbunden sein können, also mit mindestens 2n Kanten. Da jede Klausel mindestens eine Kante/einen Kantenzug der Länge t braucht, um Ci mit dem Pfad zu
verbinden, folgern wir, dass der u−v Pfad genau 2n Kanten enthält und dass jede
Klausel exakt eine Kante der Länge t braucht um auch mit dem Pfad verbunden
werden zu können. Also bildet der u − v Pfad eine erfüllbare Belegung.
6.4 Andere NP-Vollständige Probleme
Das Problem der minimalen Steiner Bäume ist ein solches NP-Vollständiges Problem. Die nachfolgend aufgeführten Probleme stellen einen kleinen Auszug vieler
weiterer NP-Vollständiger Probleme dar, mehr Informationen finden sie unter
[AstBai02].
6.4.1 Problem des Handelsreisenden
Das Handelsreisende Problem ist wohl das bekannteste NP-Vollständige Problem.
Es tritt in so gut wie jeder Routenplanungssoftware oder Logistiksoftware auf und
lässt sich sehr einfach beschreiben:
Ein Handelsreisender muss an einem Tag n Kunden in verschiedenen Städten
besuchen. Gesucht ist die kürzeste Rundreise, bei der er bei jedem Kunden genau
einmal vorbeikommt und am Ende seiner Reise wieder zu Hause ist.
6.4.2 Rucksackproblem
Das Rucksackproblem findet auch in der Logistik seine Anwendung, denn es ist
egal, ob ein Rucksack oder ein LKW vollgeladen wird. Die Aufgabenstellung ist
wie folgt:
Gegeben sind n Gegenstände mit den Gewichten (Massen) g1 , g2 , . . . , gn und den
zugehörigen Werten w1 , w2 , . . . , wn . Das Gewicht des i-ten Gegenstands beträgt
gi , sein Wert wi . Die Interpretation des Wertes hängt vom Kontext ab, so wäre
es bei Schmuck der Geldwert oder bei der Ausrüstung eines Bergsteigers der Beitrag, den der Gegenstand zur erfolgreichen Bewältigung seines Aufstiegs beträgt.
Der Rucksack ist beschränkt belastbar, sein Maximalgewicht sei K. D.h. das Gesamtgewicht aller eingepackten Gegenstände darf K nicht überschreiten. Gesucht
ist nun die Teilmenge der Gegenstände, die maximalen Wert hat, aber K nicht
überschreitet.
20
René Trumpp
Wasser für die Wüste
Anders formuliert:
Es sind die natürliche Zahlen gP
1 , g2 , . . . , gn , w1 , wP
2 , . . . , wn und K gegeben, gesucht
ist eine Teilmenge I, so dass i∈I gi ≤ K und i∈I wi = M AX.
21
7 Näherungsfunktionen
7.1 Sonderfälle
Wir haben in den vorangegangenen Kapiteln einige sehr schwere Probleme kennen gelernt, die nicht “effizient“ lösbar waren. Nun drängt sich die Frage auf,
warum wir nicht mit einer fast perfekten Lösung leben sollten, wenn es um einiges einfacher wäre, diese zu berechnen.
Doch zuerst wollen wir zwei Sonderfälle betrachten, bei denen wir auch ohne
Näherung zu einer exakten Lösung kommen.
7.1.1 Keine Steinerknoten
Gegeben ist ein Graph G, der nur aus Terminalen besteht (also keine Steinerknoten besitzt). Anschaulich ist dann der minimale Steinerbaum identisch mit
dem minimal aufspannenden Baum (siehe Kapitel 2). Die Laufzeit für diesen
Sonderfall ist also O(n2 ).
7.1.2 Nur zwei Terminale
Gegeben ist ein Graph G, der nur aus zwei Terminalen (T1 , T2 ) und n Steinerknoten besteht. Ein minimaler Steiner Baum entspricht dem kürzesten Weg zwischen
T1 und T2 . Die Problemstellung eines kürzesten Weges zwischen zwei Knoten in
einem Graphen ist jedoch leicht lösbar:
1
2
3
4
5
6
7
8
9
for each v ∈ V do {
Distanz ( v )=∞;
Vorgänger ( v )= NULL ;
}
Distanz (T1 )=0;
Vorgänger (T1 )=T1 ;
U=V;
while U6= ∅ do {
22
René Trumpp
10
11
12
13
14
15
16
17
18
Wasser für die Wüste
Finde u∈U mit Distanz ( u )= min { Distanz ( v )| v∈U };
U =U -{ u };
for each (u , v )∈E , v∈U do {
if ( Distanz ( u ) + Gewicht (u , v )) < Distanz ( v ) then {
Distanz ( v )= Distanz ( u ) + Gewicht (u , v );
Vorgänger ( v )= u ;
}
}
}
Der gezeigte Algorithmus stammt von Edsger Wybe Dijkstra. Er berechnet den
kürzesten Weg zwischen 2 Knoten, indem er zuerst den Abstand zwischen T1
und allen anderen Knoten auf ∞ setzt. Sobald ein kürzerer Abstand als der
bereits bekannte zu einem Knoten gefunden wird, wird der neu gefundene Weg als
kürzester markiert. Um nun den kürzesten Weg zwischen T1 und T2 auszulesen,
geht man von T2 immer zum Vorgänger bis T1 erreicht ist. Die Laufzeit des
Algorithmus ist O(n2 ).
7.2 Qualitätskriterien für Näherungsfunktionen
Nachdem wir die Sonderfälle betrachtet haben, schauen wir uns nun die Näherungsfunktionen an.
Es stellt sich natürlich die Frage, wie wir unsere Näherungen miteinander vergleichen. Wie können wir sagen, die eine Näherung ist besser oder schlechter als die
andere? Benutzt man für verschiedene Graphen zwei verschiedene Näherungsfunktionen, wird wahrscheinlich einmal der eine und einmal der andere besser
sein. Also können wir es schlecht an einem Beispiel festmachen. Man hat sich
also für eine andere Vergleichsmethode entschieden, man vergleicht immer den
“Worst-Case“, d.h. wie schlecht ist die Näherung im schlechtesten Fall. Hierbei
gibt man das Verhältnis (Ratio) zur optimalen Lösung an. Wir werden im nächsten Schritt eine Näherungsfunktion mit Ratio 2 betrachten, d.h. eine Näherungsfunktion, die im schlechtesten Fall das doppelte Gewicht der optimalen Lösung
hat.
7.3 Näherungsfunktion mit Ratio 2
Bevor wir uns um die eigentliche Näherungsfunktion kümmern können, brauchen
wir noch den s.g. Distanzgraphen. Bei einem gewichteten Graphen G mit den
Knoten V und den Kanten E ist der Distanzgraph D wie folgt definiert:
23
René Trumpp
Wasser für die Wüste
• D ist ungerichtet und vollständig
• Das Gewicht der Kante (u,v) in D entspricht dem Gewicht des kürzesten
Weges in G von u nach v.
Um den Distanzgraphen zu berechnen benutzen wir den Dijkstra Algorithmus
für jeden Knoten einzeln. Es reicht den Algorithmus für jeden Knoten einmal
aufzurufen und nicht für jedes Knotenpaar, denn er berechnet den kürzesten
Abstand vom Ausgangsknoten zu allen anderen Knoten. Seine Laufzeit ist n ∗
O(n2 ) = O(n3 ), wir werden sehen, dass das auch der nach unten beschränkende
Faktor in unserer Näherungsfunktion sein wird.
7.3.1 Satz
Es sei
• G ein ungerichteter gewichteter Graph mit V Knoten
• T eine Teilmenge von V
• D sei der Distanzgraph von T
• B der minimale Steinerbaum von G mit T als Terminale
• msp(D) der minimal aufspannenden Baum von D
dann gilt
Gewicht von msp(D) ≤ 2∗ Gewicht von B
24
René Trumpp
Wasser für die Wüste
7.3.2 Beweis
Abbildung 7.1: Zyklische Durchmusterung
Wir betrachten zuerst eine zyklische Durchmusterung zu B beginnend bei einem
Blatt u0 (d.h. u0 ist ein Terminal). Eine zyklische Durchmusterung stellt man sich
am einfachsten so vor, dass man um den Graphen B einmal außen herumfährt.
Es gibt also eine Folge von Knoten u0 , u1 , . . . , u2m , wobei u0 = u2m , mit :
1. Alle Kanten der Folge existieren auch in B :
∃ei ∈ EB |ei = (ui , ui+1 ), ∀0 ≤ i < 2m
2. Jede Kante ei ∈ EB kommt in der Folge genau zwei mal vor: e = (ui , uj )
und e = (uj , ui ) für i 6= j
3. Bis auf den Knoten u0 kommt jedes Blatt in der Folge nur einmal vor
Aus der zweiten Eigenschaft folgt (mit λ als Gewichtsfunktion):
2m
X
i=0
λ(ei ) =
X
2 ∗ λ(e) = 2 ∗ λ(B)
e∈EB
Wir betrachten nun eine neue Teilfolge, die aus k Terminalen besteht:
u0 = ui1 , . . . , uik
Wegen der ersten Eigenschaft bildet für alle j ∈ {1, . . . , k − 1} die Teilfolge
uij , uij +1 , . . . , uij+1
25
René Trumpp
Wasser für die Wüste
einen Weg in G, so dass
ij+1 −1
X
λ(ei ) ≥ Abstand(uij , uij+1 )inD
i=ij
gilt, da wir im Distanzgraphen immer die kürzesten Wege betrachtet haben.
Da mit der ersten Eigenschaft auch uik , uik +1 , . . . , u2m einen Weg in G bilden, gilt
auch
2m−1
X
λ(ei ) ≥ Abstand(uik , u0 )inD
i=ik
Damit folgt:
2 ∗ λ(B) =
2m
X
i=0
≥
k−1
X
λ(ei ) =
−1
k−1 ij+1
X
X
j=1
i=ij
λ(ei ) +
2m−1
X
λ(ei )
i=ik
[Abstand(uij , uij+1 )inD] + Abstand(uik , ui1 )inD
j=1
Dies entspricht der Länge des Kreises ui1 , ui2 , . . . , uik , ui1 in D. Dieses Gewicht
ist aber sicher höher als das Gewicht eines minimal aufspannenden Baumes in D.
7.3.3 Algorithmus
Kou, Markowsky und Berman nutzen diese Eigenschaft für ihren Algorithmus:
1
2
3
4
5
6
7
8
9
10
Berechne den Distanzgraphen D von G
Berechne den minimal aufspannenden Baum
Berechne zu M 0 den Teilgraphen G0 von G
Bestimme zu jeder Kante (u,v) aus M 0
Weg p (u,v) in G, der ihre
Länge hat ,
S
G0 =
p (u,v)
M 0 von D
wie folgt
einen kürzesten
setze dann
Berechne den minimal aufspannenden Baum M von G0
Lösche alle Blätter aus M , die keine Terminale sind , solange
bis nur noch Terminale Blätter sind
Die letzten beiden Schritte sind nötig, da die kürzesten Wege zu den Spannbaumkanten von D nicht eindeutig bestimmt sind und so sehr ungünstig gewählt
werden können (wie wir im folgenden Beispiel sehen werden).
Vergleicht man die einzelnen Schritte, so ist die Bestimmung der kürzesten Wege
am schwierigsten. Also wird von ihr die Laufzeit abhängig und somit zu O(n3 ).
26
René Trumpp
Wasser für die Wüste
7.3.4 Beispiel
Wenden wir nun den Algorithmus auf ein Rechwinkliges Steinerproblem an (Gitterabstand = 1):
_ _ __
_ _ __
_ _ _ _
_ _ __
_ _ _ _
_ _ _
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ _ _






 4



4

4
_ _
_ _
_ _
_ _
_ _
Abbildung 7.2: Beispielgraph G
_ _ _
_ __
_ __
_ __
_ __
Abbildung 7.4: M’ von D (Schritt 2)
_ _ __
_ _ __
_ _ _ _
_ _ __
_ _ _ _
_ __
_ __
_ __
_ __
_ _ _
_ _
_ _
_ _
_ _
_ _
Abbildung 7.6: M von G’ (Schritt 4)
 ???

??

??

??4


??
4

??

??


?

4
?
4
??


??

??

??
4 
?

4 ??
??


?? 

Abbildung 7.3: Distanzgraph D zu G
(Schritt 1)
_ _ __
_ _ __
_ _ _ _
_ _ __
_ _ _ _
_ _ _
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ _ _
_ _ _
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ __
_ _ _
_ _
_ _
_ _
_ _
_ _
Abbildung 7.5: Teilgraph G’ (Schritt 3)
_ _ __
_ _ __
_ _ _ _
_ _ __
_ _ _ _
_ _
_ _
_ _
_ _
_ _
Abbildung 7.7: Fertiger Steiner Baum B
(Schritt 5)
27
René Trumpp
Wasser für die Wüste
7.4 Näherungsfunktion mit Ratio 1,217
In [GrHoNiPr02] wird eine Näherungsfunktion beschrieben, welcher eine Näherung mit Ratio 73/60 < 1, 217 erreicht.
Gegeben ist ein quasi-bipartiter Graph G = (V, E), mit einer Teilmenge R ⊆ V ,
den Terminalen. Jeder Steiner Baum kann in s.g. volle Komponenten aufgeteilt
werden. Eine volle Komponente ist ein Steiner Baum einer Teilmenge von R,
in der jedes Terminal ein Blatt ist. Die Länge einer vollen Komponente ist die
Summe der Längen seiner Kanten. Ein Steiner Baum ist eine Ansammlung von
vollen Komponenten, die verbunden ist und gleichzeitig alle R enthält.
Das Steiner Baum Problem kann also wie folgt als Hypergraphen-Problem formuliert werden: Jede volle Komponente repräsentiert eine Hyperkante, bestehend
aus seinen Terminalen. Es sei F C die Menge dieser Hyperkanten und | t | die
Länge der vollen Komponente. Dann ist das Steiner Baum Problem, der minimal
aufspannende Sub-Hypergraph in dem gewichteten Hypergraphen (R, F C, | ◦ |).
Im folgenden wird diese Formulierung für das Steiner Baum Problem verwendet.
Nachstehender Algorithmus wird als Greedy-MSS bezeichnet:
1
2
3
4
5
6
Eingabe : Ein gewichteter Hypergraph (R, F C, | ◦ |)
i =0;
WHILE (R, {t1 , . . . , ti }) ist nicht verbunden
Suche ti+1 ∈ F C , so dass fi minimiert wird ;
i = i +1;
RETURN t1 , . . . , ti
Bei jedem Durchlauf der While-Schleife wird eine Hyperkante ausgewählt, die die
Länge auf
|t|
fi (t) =
ci (t)
minimiert. Wobei ci (t) als die Differenz zwischen der Anzahl der verbunden Komponenten von (R, t1 , . . . , ti ) und von (R, t, t1 , . . . , ti ) definiert ist.
Theorem: Greedy-MSS berechnet eine Näherung mit Ratio 73/60 < 1, 217 für
das Steiner Baum Problem in quasi-bipartiten Graphen.
Den ausführlichen Beweis für das Theorem finden Sie in [GrHoNiPr02].
7.5 Andere Näherungsfunktionen
Natürlich gibt es noch andere Näherungsfunktionen mit teilweise sogar noch besseren Ratios, alle hier aufzuführen würde aber zu weit gehen. Sollten Sie Interesse
28
René Trumpp
Wasser für die Wüste
haben, finden sich im Internet einige interessante Veröffentlichungen zu diesem
Thema.
Vielleicht sollten wir uns einfach einen eigene Näherungsfunktion überlegen.
Für ein Dreieck (mit keinem Winkel größer als 120 Grad) aus drei Punkten (a, b, c)
ist uns eine Berechnungsvorschrift für den Steinerpunkt bekannt (aus [ProSte02]):
1
2
3
4
5
6
Konstruiere das gleichseitige Dreieck 4abd,
so dass d auf der einen Seite der Linie ab liegt
und c auf der anderen .
Konstruiere den Umkreis U von 4abd.
Der Schnittpunkt der Linie cd mit dem Kreis U ist
der gesuchte Steinerpunkt S .
Als Näherung für das Steiner Baum Problem wäre es nun denkbar, die exakte Lösung für drei Punkte immer wieder zu verwenden. Die Auswahl, wie man
den Graphen zerlegt ist dabei das schwierigste Problem. Es sind verschiedene
Zerlegungen möglich, hier ein Beispiel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Wähle beliebiges u ∈ V ;
G = {u};
V = V − {u};
Finde v1 ∈ V mit Distanz (v1 , u)= max { Distanz (v, u)|v ∈ V };
V = V − {v1 };
G = G + {v1 };
Finde v2 ∈ V mit Distanz (v2 , u)= max { Distanz (v, u)|v ∈ V };
V = V − {v2 };
G = G + {v1 };
Konstruiere Steinerpunkt s in (u, v1 , v2 );
G = G + {s};
while V 6= ∅ {
Finde u ∈ V mit Distanz (u, G)= max { Distanz (u, G)};
V = V − {u};
Finde v1 ∈ G mit Distanz (v1 , u)= min { Distanz (v, u)|v ∈ G};
Finde v2 ∈ G mit Distanz (v2 , u)= min { Distanz (v, u)|v ∈ G};
Konstruiere Steinerpunkt s in (u, v1 , v2 );
G = G + {s};
}
Berechne den minimal aufspannenden Baum T von G;
Lösche alle Blätter aus T , die keine Terminale sind , solange
bis nur noch Terminale Blätter sind
29
René Trumpp
Wasser für die Wüste
7.6 Schranke
Bei vielen Näherungsverfahren kann man, je öfter man sie anwendet, sich dem
exakten Ergebnis beliebig nähern.
Bei allen Näherungsverfahren für das Steiner Baum Problem ist das leider nicht
möglich. Es gibt immer ein ε, so dass gilt
N äherung ≥ ExakteLösung + ε
Der Beweis dafür (siehe [ProSte02]) ist nicht ganz einfach. Es wird dazu zuerst das
s.g. Max3SAT Problem beschrieben und bewiesen, dass man sich dem exakten
Ergebnis nicht beliebig nähern kann. Danach wird das Steiner Baum Problem auf
das Max3SAT Problem reduziert.
Zur besten im Moment bekannten untere Schranke 1,0074 ist leider noch kein
effektiver Näherungsalgorithmus bekannt ([GrHoNiPr02]).
30
8 Zusammenfassung
8.1 Rückblick
Auf den letzten Seiten habe ich versucht Ihnen das Steiner Problem näher zu
bringen. Sie haben gesehen, wie sich ganz einfache Probleme durch kleine Änderungen der Fragestellung zu sehr schweren Problemen geändert haben.
Es wurden verschiedene Näherungsfunktionen vorgestellt und gezeigt, dass es
auch innerhalb der Näherungsfunktionen große Unterschiede gibt.
Ich hoffe ich konnte Ihnen zeigen, warum es wichtig ist, sich auch mit den theoretischen Problemen der Informatik zu beschäftigen, denn es gibt kaum Probleme,
die sich nicht in der realen Welt anwenden lassen.
Ich konnte viele neue Kenntnisse aus meiner Arbeit gewinnen. Ich habe mich tief
in das Thema eingearbeitet und mir gegen Ende eigene Gedanken dazu gemacht.
Am schwersten fiel mir, die doch sehr tief geführten Beweise zu verstehen. Es wird
in vielen der Quellen ein großes Basiswissen vorausgesetzt. Danach einen eigenen
Ansatz zu finden war sehr schwer und der gefundene Ansatz ist bestimmt nicht
optimal, doch war seine Entwicklung für mich sehr interessant und lehrreich.
Die Implementierung einiger der vorgestellten Funktionen am Ende dieser Arbeit
hat mir sehr viel Spass gemacht, ich konnte dadurch Strecken- und Zeitmessungen
durchführen. So wurde mir bestätigt, wie sich die einzelnen Funktionen (nicht nur
in der Theorie) unterscheiden.
31
9 Anhang
9.1 Implementierung
Nachdem ich mich mit den Algorithmen in der Theorie beschäftigt hatte, wollte
ich einige nun in Java programmieren, um ihr Laufzeitverhalten unter realen Bedingungen zu testen. Dabei stellte ich fest, dass sich manche Teile der Algorithmen
zwar sehr leicht formulieren lassen, aber die Umsetzung in eine Programmiersprache nicht einfach ist.
9.1.1 Klassendiagramm
Abbildung 9.1: Klassen Übersicht
32
René Trumpp
Wasser für die Wüste
9.1.2 Quelltexte
Graph Klasse
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class Graph {
protected Vector nodeList ;
protected double [][] edgeMatrix ;
protected static int NO_EDGE = -1;
public Graph () {
nodeList = new Vector ();
edgeMatrix = new double [0][0];
}
protected void ge ne rat eRa nd om ( int numberOfNodes ) {
int x , y ;
boolean isTerminal ;
int n u m b e r O f T e r m i n a l s = 0;
Random rand = new Random ();
nodeList . r e m o v e A l l E l e m e n t s ();
if ( numberOfNodes < 2)
numberOfNodes = 2;
if ( numberOfNodes > 500)
numberOfNodes = 500;
// generate numberOfNodes -2 random nodes
// can be terminals or steiner nodes
for ( int i = 0; i < numberOfNodes - 2; i ++) {
x = rand . nextInt (500);
y = rand . nextInt (500);
isTerminal = rand . nextBoolean ();
addNode ( new Node (x , y , isTerminal ));
if ( isTerminal )
n u m b e r O f T e r m i n a l s ++;
}
// We need at least 2 nodes
for ( int i = 0; i < 2; i ++) {
x = rand . nextInt (500);
y = rand . nextInt (500);
isTerminal = rand . nextBoolean ();
if ( n u m b e r O f T e r m i n a l s < 2) {
addNode ( new Node (x , y , true ));
n u m b e r O f T e r m i n a l s ++;
} else {
addNode ( new Node (x , y , isTerminal ));
}
}
g e n e r a t e A l l E d g e s ();
}
protected void g e n e r a t e A l l E d g e s () {
for ( int i = 0; i < nodeList . size (); i ++) {
for ( int j = i + 1; j < nodeList . size (); j ++) {
addEdge (i , j );
}
}
}
protected void addNode ( Node node ) {
nodeList . add ( node );
e x p a n d E d g e M a t r i x ();
}
protected void e x p a n d E d g e M a t r i x () {
double [][] newEdgeMatrix = new double [ edgeMatrix . length + 1][ edgeMatrix . length + 1];
for ( int i = 0; i < edgeMatrix . length ; i ++) {
for ( int j = 0; j < edgeMatrix . length ; j ++) {
newEdgeMatrix [ i ][ j ] = edgeMatrix [ i ][ j ];
}
}
for ( int i = 0; i < edgeMatrix . length + 1; i ++) {
newEdgeMatrix [ edgeMatrix . length ][ i ] = NO_EDGE ;
newEdgeMatrix [ i ][ edgeMatrix . length ] = NO_EDGE ;
}
edgeMatrix = newEdgeMatrix ;
}
33
René Trumpp
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
Wasser für die Wüste
protected void removeNode ( int nodeNr ) {
if ( nodeNr < nodeList . size ())
nodeList . remove ( nodeNr );
}
protected void addEdge ( int from , int to , double length ) {
if ( from > nodeList . size () || from < 0 || to > nodeList . size ()
|| to < 0 || length <= 0)
return ;
edgeMatrix [ from ][ to ] = length ;
edgeMatrix [ to ][ from ] = length ;
}
protected void addEdge ( int from , int to ) {
if ( from > nodeList . size () || from < 0 || to > nodeList . size ()
|| to < 0)
return ;
double length = Math . sqrt ( Math . pow (
((( Node ) nodeList . get ( from )). getX () - (( Node ) nodeList . get ( to ))
. getX ()) , 2)
+ Math . pow (
((( Node ) nodeList . get ( from )). getY () - (( Node ) nodeList
. get ( to )). getY ()) , 2));
edgeMatrix [ from ][ to ] = length ;
edgeMatrix [ to ][ from ] = length ;
}
protected void removeEdge ( int from , int to ) {
if ( from > nodeList . size () || from < 0 || to > nodeList . size ()
|| to < 0)
return ;
edgeMatrix [ from ][ to ] = NO_EDGE ;
edgeMatrix [ to ][ from ] = NO_EDGE ;
}
public boolean load ( String fileName ) {
if (!(( new File ( fileName )). exists ())) {
System . out . println ( " Can not read file " + fileName
+ " , does not exist " );
return false ;
}
nodeList . r e m o v e A l l E l e m e n t s ();
edgeMatrix = new double [0][0];
int numberOfEdges = 0;
String line ;
int linenumber = 0;
try {
F i l eI n p u t St r e a m fin = new F i l eI n p u tS t r e a m ( fileName );
Bu ffe re dRe ad er inputFile = new Bu ff ere dR ead er (
new I n p u t S t r e a m R e a d e r ( fin ));
while (( line = inputFile . readLine ()) != null ) {
linenumber ++;
if (! line . toLowerCase (). startsWith ( " \\\\ " )) {
// Node
if ( line . toLowerCase (). startsWith ( " n " )) {
line = line . trim (). substring (2 , line . length () - 1);
String linesplit [] = line . split ( " ," );
if ( linesplit . length < 2 || linesplit . length > 3) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
double x ;
double y ;
try {
x = new Double ( linesplit [0]). doubleValue ();
y = new Double ( linesplit [1]). doubleValue ();
} catch ( Exception e ) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
boolean isTerminal = true ;
if ( linesplit . length == 3) {
if ( linesplit [2]. e q u a l s I g n o r e C a s e ( " s " )) {
isTerminal = false ;
}
}
addNode ( new Node (x , y , isTerminal ));
34
René Trumpp
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
Wasser für die Wüste
} else
// Edge
if ( line . toLowerCase (). startsWith ( " e " )) {
line = line . trim (). substring (2 , line . length () - 1);
String linesplit [] = line . split ( " ," );
if ( linesplit . length < 2 || linesplit . length > 3) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
int x ;
int y ;
double length ;
try {
x = new Integer ( linesplit [0]). intValue ();
y = new Integer ( linesplit [1]). intValue ();
} catch ( Exception e ) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
if ( x >= nodeList . size () || y >= nodeList . size ()) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
if ( linesplit . length == 3) {
try {
length = new Double ( linesplit [2]). doubleValue ();
addEdge (x , y , length );
numberOfEdges ++;
} catch ( Exception e ) {
System . out . println ( " Malformed inputfile , line "
+ linenumber );
return false ;
}
} else {
addEdge (x , y );
numberOfEdges ++;
}
}
}
}
} catch ( Exception e ) {
e . pr i n t St a c k T ra c e ();
return false ;
}
if ( nodeList . size () == 0) {
System . out . println ( " Malformed inputfile , no nodes in file " );
return false ;
}
int n u m b e r O f T e r m i n a l s = 0;
for ( int i = 0; i < nodeList . size (); i ++) {
if ((( Node ) nodeList . get ( i )). isTerminal ())
n u m b e r O f T e r m i n a l s ++;
}
if ( n u m b e r O f T e r m i n a l s < 2) {
System . out
. println ( " Malformed inputfile , not enough terminals in file " );
return false ;
}
if ( numberOfEdges == 0) {
g e n e r a t e A l l E d g e s ();
}
return true ;
}
public boolean save ( String fileName ) {
if ((( new File ( fileName )). exists ())) {
System . out . println ( " Can not write file " + fileName
+ " , already exists " );
return false ;
}
F i l e O u t p u t S t r e a m fout ;
try {
fout = new F i l e O u t p u t S t r e a m ( fileName );
Bu ffe re dWr it er outFile = new Bu ff ere dWr it er ( new O u t p u t S t r e a m W r i t e r (
fout ));
35
René Trumpp
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
Wasser für die Wüste
for ( int i = 0; i < nodeList . size (); i ++) {
outFile . write ( " \\\\ Knoten Nr . " + i + " :\ n " );
Node node = ( Node ) nodeList . get ( i );
outFile . write ( " N ( " + node . getX () + " ," + node . getY ());
if ( node . isTerminal ()) {
outFile . write ( " ,T " );
} else {
outFile . write ( " ,S " );
}
outFile . write ( " )\ n " );
}
int edgecounter = 0;
for ( int i = 1; i < nodeList . size (); i ++) {
for ( int j = 0; j < i ; j ++) {
if ( edgeMatrix [ i ][ j ] != NO_EDGE ) {
outFile . write ( " \\\\ Kante Nr . " + edgecounter + " :\ n " );
outFile . write ( " E ( " + i + " ," + j + " ,"
+ edgeMatrix [ i ][ j ] + " )\ n " );
edgecounter ++;
}
}
}
outFile . close ();
fout . close ();
} catch ( F i l e N o t F o u n d E x c e p t i o n e ) {
e . pr i n t St a c k T ra c e ();
System . out . println ( " Can not write file " + fileName );
return false ;
} catch ( IOException e ) {
e . pr i n t St a c k T ra c e ();
System . out . println ( " Can not write file " + fileName );
return false ;
}
return true ;
}
public double getLength () {
double length = 0;
for ( int i = 1; i < nodeList . size (); i ++) {
for ( int j = 0; j < i ; j ++) {
if ( edgeMatrix [ i ][ j ] != NO_EDGE ) {
length += edgeMatrix [ i ][ j ];
}
}
}
return length ;
}
public int g e t N u m b e r O f E d g e s () {
int numberOfEdges = 0;
for ( int i = 1; i < nodeList . size (); i ++) {
for ( int j = 0; j < i ; j ++) {
if ( edgeMatrix [ i ][ j ] != NO_EDGE ) {
numberOfEdges ++;
}
}
}
return numberOfEdges ;
}
public void draw ( String filepath ) {
draw ( filepath , 1);
}
public void draw ( String filepath , int scalefactor ) {
double minx = Double . MAX_VALUE , maxx = 0 , miny = Double . MAX_VALUE , maxy = 0;
for ( int i = 0; i < nodeList . size (); i ++) {
Node node = (( Node ) nodeList . get ( i ));
if ( node . getX () < minx )
minx = node . getX ();
if ( node . getY () < miny )
miny = node . getY ();
if ( node . getX () > maxx )
maxx = node . getX ();
if ( node . getY () > maxy )
maxy = node . getY ();
}
int shiftfactorx = (( int ) (0 - minx + 10)) * scalefactor ;
int shiftfactory = (( int ) (0 - miny + 10)) * scalefactor ;
int sizex = (( int ) ( maxx + 10)) * scalefactor ;
int sizey = (( int ) ( maxy + 10)) * scalefactor ;
BufferedImage img = new BufferedImage ( sizex , sizey ,
36
René Trumpp
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
Wasser für die Wüste
BufferedImage . TYPE_INT_RGB );
Graphics graphic = img . getGraphics ();
graphic . setColor ( Color . WHITE );
graphic . fillRect (0 , 0 , sizex - 1 , sizey - 1);
graphic . setFont ( new Font ( " Serif " , Font . BOLD , 4 * scalefactor ));
// Draw Edges
for ( int i = 1; i < nodeList . size (); i ++) {
for ( int j = 0; j < i ; j ++) {
if ( edgeMatrix [ i ][ j ] != NO_EDGE ) {
Node from = ( Node ) nodeList . get ( i );
Node to = ( Node ) nodeList . get ( j );
double length = edgeMatrix [ i ][ j ];
from . drawLineTo ( to , graphic , scalefactor , shiftfactorx ,
shiftfactory , length );
}
}
}
// Draw Nodes
for ( int i = 0; i < nodeList . size (); i ++) {
(( Node ) nodeList . get ( i )). draw ( graphic , shiftfactorx , shiftfactory ,
scalefactor , i );
}
B y t e A r r a y O u t p u t S t r e a m out = new B y t e A r r a y O u t p u t S t r e a m (0 xfff );
J P E G I m a g e E n c o d e r encoder = JPEGCodec . c r e a t e J P E G E n c o d e r ( out );
J P EG E n c o de P a r a m param ;
param = encoder . g e t D e f a u l t J P E G E n c o d e P a r a m ( img );
param . setQuality (1 f , true );
try {
encoder . encode ( img , param );
F i l e O u t p u t S t r e a m fos = new F i l e O u t p u t S t r e a m ( filepath );
fos . write ( out . toByteArray ());
fos . close ();
out . close ();
} catch ( I m a g e F o r m a t E x c e p t i o n e ) {
e . pr i n t St a c k T ra c e ();
} catch ( IOException e ) {
e . pr i n t St a c k T ra c e ();
}
}
int g e t N u m b e r O f T e r m i n a l s () {
int counter = 0;
for ( int i = 0; i < nodeList . size (); i ++) {
if ((( Node ) nodeList . get ( i )). isTerminal ())
counter ++;
}
return counter ;
}
int g e t N u m b e r O f S t e i n e r N o d e s () {
return nodeList . size () - g e t N u m b e r O f T e r m i n a l s ();
}
}
Node Klasse
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Node {
double x ;
double y ;
boolean isTerminal ;
public Node ( double x , double y ) {
this (x , y , true );
}
public Node ( double x , double y , boolean isTerminal ) {
this . x = x ;
this . y = y ;
this . isTerminal = isTerminal ;
}
public void draw ( Graphics graphic , int shiftfactorx , int shiftfactory ,
int scalefactor , int nodeNr ) {
if ( isTerminal ) {
graphic . setColor ( Color . BLACK );
} else {
graphic . setColor ( Color . GRAY );
}
int x1 = (( int ) x ) * scalefactor + shiftfactorx - 5 * scalefactor ;
int y1 = (( int ) y ) * scalefactor + shiftfactory - 5 * scalefactor ;
graphic . fillOval ( x1 , y1 , 7 * scalefactor , 7 * scalefactor );
37
René Trumpp
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Wasser für die Wüste
graphic . setColor ( Color . BLACK );
graphic . drawString ( " " + nodeNr , x1 , y1 );
}
public boolean isTerminal () {
return isTerminal ;
}
public double getX () {
return x ;
}
public double getY () {
return y ;
}
public void setX ( double x ) {
this . x = x ;
}
public void setY ( double y ) {
this . y = y ;
}
public void setTerminal ( boolean isTerminal ) {
this . isTerminal = isTerminal ;
}
public void drawLineTo ( Node to , Graphics graphic , int scalefactor ,
int shiftfactorx , int shiftfactory , double length ) {
graphic . setColor ( Color . BLACK );
int x1 = (( int ) x ) * scalefactor + shiftfactorx - 2 * scalefactor ;
int y1 = (( int ) y ) * scalefactor + shiftfactory - 2 * scalefactor ;
int x2 = (( int ) to . getX ()) * scalefactor + shiftfactorx - 2
* scalefactor ;
int y2 = (( int ) to . getY ()) * scalefactor + shiftfactory - 2
* scalefactor ;
graphic . drawLine ( x1 , y1 , x2 , y2 );
graphic . setColor ( Color . GRAY );
graphic . drawString ( " " + Math . rint ( length * 100) / 100.0 , ( x1 + x2 ) / 2 ,
( y1 + y2 ) / 2);
}
}
Minimal Spanning Tree Klasse
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class M i n i m a l S p a n n i n g T r e e extends Graph {
Graph g ;
public M i n i m a l S p a n n i n g T r e e ( Graph g ) {
super ();
this . g = g ;
nodeList = g . nodeList ;
edgeMatrix = new double [ nodeList . size ()][ nodeList . size ()];
for ( int i = 0; i < nodeList . size (); i ++) {
for ( int j = 0; j < nodeList . size (); j ++)
edgeMatrix [ i ][ j ] = NO_EDGE ;
}
int Vorgaenger [] = calculate ();
generateEdges ( Vorgaenger );
}
private void generateEdges ( int [] vorgaenger )
for ( int i = 1; i < nodeList . size ();
edgeMatrix [ i ][ vorgaenger [ i ]]
edgeMatrix [ vorgaenger [ i ]][ i ]
}
}
{
i ++) {
= g . edgeMatrix [ i ][ vorgaenger [ i ]];
= g . edgeMatrix [ i ][ vorgaenger [ i ]];
private int [] calculate () {
boolean S [] = new boolean [ g . nodeList . size ()];
for ( int i = 0; i < g . nodeList . size (); i ++) {
S [ i ] = false ;
}
double Distanz [] = new double [ g . nodeList . size ()];
int Vorgaenger [] = new int [ g . nodeList . size ()];
// Waehle beliebigen Knoten s ;
// S ={ s };
S [0] = true ;
38
René Trumpp
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
Wasser für die Wüste
// for each v from V \{ s } do {
for ( int i = 1; i < g . nodeList . size (); i ++) {
// Distanz ( v )= Kante (s , v );
Distanz [ i ] = g . edgeMatrix [0][ i ];
if ( g . edgeMatrix [0][ i ] == NO_EDGE )
Distanz [ i ] = Double . MAX_VALUE ;
// Vorgaenger ( v )= s ;
Vorgaenger [ i ] = 0;
}
// Distanz ( s )=0;
Distanz [0] = 0;
int N u m b e r O f N o d e s I n S = 1;
// while S != V do {
while ( N u m b e r O f N o d e s I n S != g . nodeList . size ()) {
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
int u = findMin (S , Distanz );
// S = S +{ u };
S [ u ] = true ;
N u m b e r O f N o d e s I n S = 0;
for ( int i = 0; i < g . nodeList . size (); i ++) {
if ( S [ i ] == true )
N u m b e r O f N o d e s I n S ++;
}
// for each v from V \ S do {
for ( int v = 0; v < g . nodeList . size (); v ++) {
if ( S [ v ] == true || g . edgeMatrix [ u ][ v ] == NO_EDGE )
continue ;
// if Kante (u , v ) < Distanz ( v ) then {
if ( g . edgeMatrix [ u ][ v ] <= Distanz [ v ]) {
// Distanz ( v )= Kante (u , v );
Distanz [ v ] = g . edgeMatrix [ u ][ v ];
// ı̈> 1
Vorgnger ( v )= u ;
2
Vorgaenger [ v ] = u ;
}
}
}
return Vorgaenger ;
}
private int findMin ( boolean [] S , double [] Distanz ) {
int minNode = 0;
double minDistanz = Double . MAX_VALUE ;
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
for ( int u = 0; u < g . nodeList . size (); u ++) {
if ( S [ u ] == true )
continue ;
if ( Distanz [ u ] < minDistanz ) {
minDistanz = Distanz [ u ];
minNode = u ;
}
}
return minNode ;
}
}
Steiner Baum Trivial Klasse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class S t e i n e r B a u m T r i v i a l extends Graph {
private boolean [] visited ;
private double minimalsize ;
public S t e i n e r B a u m T r i v i a l ( Graph g ) {
minimalsize = Double . MAX_VALUE ;
int n u m b e r O f S t e i n e r N o d e s = 0;
for ( int i = 0; i < g . nodeList . size (); i ++) {
if (!(( Node ) g . nodeList . get ( i )). isTerminal )
n u m b e r O f S t e i n e r N o d e s ++;
}
visited = new boolean [ n u m b e r O f S t e i n e r N o d e s ];
for ( int i = 0; i < visited . length ; i ++) {
visited [ i ] = false ;
}
ge tS hor te stM ST (g , visited . length - 1);
}
39
René Trumpp
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Wasser für die Wüste
private void ge tS hor te stM ST ( Graph g , int deph ) {
visited [ deph ] = true ;
if ( deph > 0)
ge tSh or tes tM ST (g , deph - 1);
if ( deph == 0)
calculate ( g );
visited [ deph ] = false ;
if ( deph > 0)
ge tSh or tes tM ST (g , deph - 1);
if ( deph == 0)
calculate ( g );
}
private void calculate ( Graph g ) {
Graph g1 = new Graph ();
boolean linesToUse [] = new boolean [ g . nodeList . size ()];
int counter = 0;
for ( int i = 0; i < g . nodeList . size (); i ++) {
Node node = ( Node ) g . nodeList . get ( i );
if ( node . isTerminal ()) {
g1 . addNode ( node );
linesToUse [ i ] = true ;
continue ;
}
if ( visited [ counter ]) {
g1 . addNode ( node );
linesToUse [ i ] = true ;
} else {
linesToUse [ i ] = false ;
}
counter ++;
}
g1 . edgeMatrix = new double [ g1 . nodeList . size ()][ g1 . nodeList . size ()];
int column = 0;
for ( int i = 0; i < g . nodeList . size (); i ++) {
if (! linesToUse [ i ])
continue ;
int row = 0;
for ( int j = 0; j < g . nodeList . size (); j ++) {
if (! linesToUse [ j ])
continue ;
g1 . edgeMatrix [ row ][ column ] = g . edgeMatrix [ i ][ j ];
row ++;
}
column ++;
}
M i n i m a l S p a n n i n g T r e e mst = new M i n i m a l S p a n n i n g T r e e ( g1 );
if ( mst . getLength () < minimalsize ) {
minimalsize = mst . getLength ();
nodeList = mst . nodeList ;
edgeMatrix = mst . edgeMatrix ;
}
}
}
Rechtwinkliger Steiner Graph Klasse
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class R e c h t w i n k l i g e r S t e i n e r G r a p h extends Graph {
Graph g ;
public R e c h t w i n k l i g e r S t e i n e r G r a p h ( Graph g ) {
super ();
this . g = g ;
nodeList = new Vector ();
edgeMatrix = new double [0][0];
for ( int i = 0; i < g . nodeList . size (); i ++) {
if ((( Node ) g . nodeList . get ( i )). isTerminal ) {
nodeList . add (( Node ) g . nodeList . get ( i ));
}
}
int t e r m i n a l N o d e L i s t S i z e = nodeList . size ();
double xPos [] = new double [ t e r m i n a l N o d e L i s t S i z e ];
double yPos [] = new double [ t e r m i n a l N o d e L i s t S i z e ];
double xPosSorted [] = new double [ t e r m i n a l N o d e L i s t S i z e ];
double yPosSorted [] = new double [ t e r m i n a l N o d e L i s t S i z e ];
for ( int i = 0; i < nodeList . size (); i ++) {
xPos [ i ] = (( Node ) nodeList . get ( i )). getX ();
yPos [ i ] = (( Node ) nodeList . get ( i )). getY ();
40
René Trumpp
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Wasser für die Wüste
xPosSorted [ i ] = xPos [ i ];
yPosSorted [ i ] = yPos [ i ];
}
// sort entries
double temp ;
for ( int i = 0; i < xPosSorted . length ; i ++) {
for ( int j = i + 1; j < yPosSorted . length ; j ++) {
if ( xPosSorted [ i ] > xPosSorted [ j ]) {
temp = xPosSorted [ i ];
xPosSorted [ i ] = xPosSorted [ j ];
xPosSorted [ j ] = temp ;
}
if ( yPosSorted [ i ] > yPosSorted [ j ]) {
temp = yPosSorted [ i ];
yPosSorted [ i ] = yPosSorted [ j ];
yPosSorted [ j ] = temp ;
}
}
}
// Delete double entries
int doubleXPos = xPosSorted . length ;
int doubleYPos = yPosSorted . length ;
for ( int i = 0; i < xPosSorted . length - 1; i ++) {
if ( xPosSorted [ i ] == xPosSorted [ i + 1]) {
xPosSorted [ i + 1] = -1;
doubleXPos - -;
}
if ( yPosSorted [ i ] == yPosSorted [ i + 1]) {
yPosSorted [ i + 1] = -1;
doubleYPos - -;
}
}
double x P o s S o r t e d W i t h o u t D o u b l e s [] = new double [ doubleXPos ];
double y P o s S o r t e d W i t h o u t D o u b l e s [] = new double [ doubleYPos ];
doubleXPos = 0;
doubleYPos = 0;
for ( int i = 0; i < xPosSorted . length ; i ++) {
if ( xPosSorted [ i ] != -1) {
x P o s S o r t e d W i t h o u t D o u b l e s [ doubleXPos ] = xPosSorted [ i ];
doubleXPos ++;
}
if ( yPosSorted [ i ] != -1) {
y P o s S o r t e d W i t h o u t D o u b l e s [ doubleYPos ] = yPosSorted [ i ];
doubleYPos ++;
}
}
// Generate Nodes
nodeList = new Vector ();
for ( int i = 0; i < x P o s S o r t e d W i t h o u t D o u b l e s . length ; i ++) {
for ( int j = 0; j < y P o s S o r t e d W i t h o u t D o u b l e s . length ; j ++) {
addNode ( new Node ( x P o s S o r t e d W i t h o u t D o u b l e s [ i ] ,
y P o s S o r t e d W i t h o u t D o u b l e s [ j ] , false ));
}
}
// Mark Terminals
for ( int i = 0; i < nodeList . size (); i ++) {
Node node = ( Node ) nodeList . get ( i );
for ( int j = 0; j < xPos . length ; j ++) {
if ( node . getX () == xPos [ j ] && node . getY () == yPos [ j ]) {
node . setTerminal ( true );
}
}
}
// Edges
edgeMatrix = new double [ nodeList . size ()][ nodeList . size ()];
for ( int i = 0; i < nodeList . size (); i ++) {
for ( int j = 0; j < nodeList . size (); j ++) {
edgeMatrix [ i ][ j ] = NO_EDGE ;
}
}
for ( int i = 0; i < x P o s S o r t e d W i t h o u t D o u b l e s . length ; i ++) {
for ( int j = 0; j < y P o s S o r t e d W i t h o u t D o u b l e s . length - 1; j ++) {
int from = 0;
int to = 0;
for ( int n = 0; n < nodeList . size (); n ++) {
Node node = ( Node ) nodeList . get ( n );
if ( node . getX () == x P o s S o r t e d W i t h o u t D o u b l e s [ i ]
&& node . getY () == y P o s S o r t e d W i t h o u t D o u b l e s [ j ]) {
from = n ;
}
if ( node . getX () == x P o s S o r t e d W i t h o u t D o u b l e s [ i ]
&& node . getY () == y P o s S o r t e d W i t h o u t D o u b l e s [ j + 1]) {
to = n ;
41
René Trumpp
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
Wasser für die Wüste
}
}
addEdge ( from , to );
}
}
for ( int i = 0; i < y P o s S o r t e d W i t h o u t D o u b l e s . length ; i ++) {
for ( int j = 0; j < x P o s S o r t e d W i t h o u t D o u b l e s . length - 1; j ++) {
int from = 0;
int to = 0;
for ( int n = 0; n < nodeList . size (); n ++) {
Node node = ( Node ) nodeList . get ( n );
if ( node . getX () == x P o s S o r t e d W i t h o u t D o u b l e s [ j ]
&& node . getY () == y P o s S o r t e d W i t h o u t D o u b l e s [ i ]) {
from = n ;
}
if ( node . getX () == x P o s S o r t e d W i t h o u t D o u b l e s [ j + 1]
&& node . getY () == y P o s S o r t e d W i t h o u t D o u b l e s [ i ]) {
to = n ;
}
}
addEdge ( from , to );
}
}
}
}
Naeherung Ratio 2 Klasse
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class N a eh e r u n gR a t i o 2 extends Graph {
Graph g ;
boolean [] visited ;
public N a e he r u n gR a t i o 2 ( Graph g ) {
this . g = g ;
calculate ();
}
private void calculate () {
// Berechne den Di st anz gra ph en D von G
DistanceGraph d = new DistanceGraph ( g );
// Berechne den minimal aufspannenden Baum M0 von D
M i n i m a l S p a n n i n g T r e e m0 = new M i n i m a l S p a n n i n g T r e e ( d );
// Berechne zu M0 den Teilgraphen G0 von G wie folgt
Graph g0 = new Graph ();
g0 . nodeList = g . nodeList ;
g0 . edgeMatrix = new double [ g . nodeList . size ()][ g . nodeList . size ()];
for ( int i = 0; i < g . nodeList . size (); i ++) {
for ( int j = 0; j < g . nodeList . size (); j ++) {
g0 . edgeMatrix [ i ][ j ] = NO_EDGE ;
}
}
// Bestimme zu jeder Kante (u , v ) aus M0 einen ^
A¨kurzesten
for ( int i = 0; i < m0 . nodeList . size () - 1; i ++) {
for ( int j = i + 1; j < m0 . nodeList . size (); j ++) {
// Weg p (u , v ) in G , der ihre Laenge hat , setze dann
// G0 = U p (u , v )
addPath ( g0 , g , i , j , m0 . edgeMatrix [ i ][ j ]);
}
}
// Berechne den minimal aufspannenden Baum M von G0
M i n i m a l S p a n n i n g T r e e m = new M i n i m a l S p a n n i n g T r e e ( g0 );
// Loesche alle Blaetter aus M , die keine Terminale sind , solange
boolean onlyTerminals ;
// bis nur noch Terminale Blaetter sind
do {
onlyTerminals = true ;
for ( int i = 0; i < m . nodeList . size (); i ++) {
if ((( Node ) m . nodeList . get ( i )). isTerminal )
continue ;
int n u m b e r O f N e i g h b o u r s = 0;
for ( int j = 0; j < m0 . nodeList . size (); j ++) {
if ( m . edgeMatrix [ i ][ j ] != NO_EDGE )
n u m b e r O f N e i g h b o u r s ++;
}
if ( n u m b e r O f N e i g h b o u r s == 1) {
for ( int j = 0; j < m0 . nodeList . size (); j ++) {
m . edgeMatrix [ i ][ j ] = NO_EDGE ;
m . edgeMatrix [ j ][ i ] = NO_EDGE ;
}
onlyTerminals = false ;
}
}
} while (! onlyTerminals );
42
René Trumpp
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
Wasser für die Wüste
boolean emptyLines [] = new boolean [ m . nodeList . size ()];
nodeList = new Vector ();
for ( int i = 0; i < m . nodeList . size (); i ++) {
emptyLines [ i ] = false ;
int n u m b e r O f N e i g h b o u r s = 0;
for ( int j = 0; j < m0 . nodeList . size (); j ++) {
if ( m . edgeMatrix [ i ][ j ] != NO_EDGE )
n u m b e r O f N e i g h b o u r s ++;
}
if ( n u m b e r O f N e i g h b o u r s != 0) {
nodeList . add ( m . nodeList . get ( i ));
} else {
emptyLines [ i ] = true ;
}
}
= new double [ nodeList . size ()][ nodeList . size ()];
= 0;
= 0; i < m . nodeList . size (); i ++) {
( emptyLines [ i ])
continue ;
int row = 0;
for ( int j = 0; j < m . nodeList . size (); j ++) {
if ( emptyLines [ j ])
continue ;
edgeMatrix [ row ][ column ] = m . edgeMatrix [ i ][ j ];
row ++;
}
column ++;
edgeMatrix
int column
for ( int i
if
}
}
private void addPath ( Graph g0 , Graph g , int from , int to , double length ) {
int vorgaenger [] = searchPath (g , from , to , length );
g0 . edgeMatrix [ to ][ vorgaenger [ to ]] = g . edgeMatrix [ to ][ vorgaenger [ to ]];
g0 . edgeMatrix [ vorgaenger [ to ]][ to ] = g . edgeMatrix [ to ][ vorgaenger [ to ]];
while ( to != from ) {
to = vorgaenger [ to ];
g0 . edgeMatrix [ to ][ vorgaenger [ to ]] = g . edgeMatrix [ to ][ vorgaenger [ to ]];
g0 . edgeMatrix [ vorgaenger [ to ]][ to ] = g . edgeMatrix [ to ][ vorgaenger [ to ]];
}
}
private int [] searchPath ( Graph g2 , int from , int to , double maxlength ) {
// Modifizierter MST Algorithmus
boolean S [] = new boolean [ g2 . nodeList . size ()];
for ( int i = 0; i < g2 . nodeList . size (); i ++) {
S [ i ] = false ;
}
double Distanz [] = new double [ g2 . nodeList . size ()];
int Vorgaenger [] = new int [ g2 . nodeList . size ()];
// Waehle Startknoten s ;
// S ={ s };
S [ from ] = true ;
// for each v from V \{ s } do {
for ( int i = 0; i < g2 . nodeList . size (); i ++) {
// Distanz ( v )= Kante (s , v );
Distanz [ i ] = g2 . edgeMatrix [ from ][ i ];
if ( g2 . edgeMatrix [ from ][ i ] == NO_EDGE )
Distanz [ i ] = Double . MAX_VALUE ;
// Vorgaenger ( v )= s ;
Vorgaenger [ i ] = from ;
}
// Distanz ( s )=0;
Distanz [ from ] = 0;
int N u m b e r O f N o d e s I n S = 1;
// while S != V do {
while ( N u m b e r O f N o d e s I n S != g2 . nodeList . size ()) {
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
int u = findMin (S , Distanz , g2 );
// S = S +{ u };
S [ u ] = true ;
N u m b e r O f N o d e s I n S = 0;
for ( int i = 0; i < g2 . nodeList . size (); i ++) {
if ( S [ i ] == true )
N u m b e r O f N o d e s I n S ++;
}
// for each v from V \ S do {
for ( int v = 0; v < g2 . nodeList . size (); v ++) {
if ( S [ v ] == true || g2 . edgeMatrix [ u ][ v ] == NO_EDGE )
continue ;
// if Kante (u , v ) < Distanz ( v ) then {
43
René Trumpp
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
Wasser für die Wüste
if ( Distanz [ u ] + g2 . edgeMatrix [ u ][ v ] <= Distanz [ v ]) {
// Distanz ( v )= Kante (u , v );
Distanz [ v ] = Distanz [ u ] + g2 . edgeMatrix [ u ][ v ];
// Vorgaenger ( v )= u ;
Vorgaenger [ v ] = u ;
if ( Distanz [ v ] == maxlength && v == to )
return Vorgaenger ;
}
}
}
return Vorgaenger ;
}
private int findMin ( boolean [] S , double [] Distanz , Graph g ) {
int minNode = 0;
double minDistanz = Double . MAX_VALUE ;
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
for ( int u = 0; u < g . nodeList . size (); u ++) {
if ( S [ u ] == true )
continue ;
if ( Distanz [ u ] < minDistanz ) {
minDistanz = Distanz [ u ];
minNode = u ;
}
}
return minNode ;
}
}
Distance Graph Klasse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class DistanceGraph extends Graph {
public DistanceGraph ( Graph g ) {
nodeList = g . nodeList ;
edgeMatrix = new double [ g . nodeList . size ()][ g . nodeList . size ()];
for ( int i = 0; i < g . nodeList . size (); i ++) {
for ( int j = 0; j < g . nodeList . size (); j ++) {
edgeMatrix [ i ][ j ] = NO_EDGE ;
}
}
for ( int i = 0; i < g . nodeList . size (); i ++) {
double distance [] = calculate (g , i );
distance [ i ] = NO_EDGE ;
for ( int j = 0; j < g . nodeList . size (); j ++) {
edgeMatrix [ i ][ j ] = distance [ j ];
}
}
}
private double [] calculate ( Graph g , int nodeNr ) {
// Modifizierter MST Algorithmus
boolean S [] = new boolean [ g . nodeList . size ()];
for ( int i = 0; i < g . nodeList . size (); i ++) {
S [ i ] = false ;
}
double Distanz [] = new double [ g . nodeList . size ()];
int Vorgaenger [] = new int [ g . nodeList . size ()];
// Waehle Startknoten s ;
// S ={ s };
S [ nodeNr ] = true ;
// for each v from V \{ s } do {
for ( int i = 0; i < g . nodeList . size (); i ++) {
// Distanz ( v )= Kante (s , v );
Distanz [ i ] = g . edgeMatrix [ nodeNr ][ i ];
if ( g . edgeMatrix [ nodeNr ][ i ] == NO_EDGE )
Distanz [ i ] = Double . MAX_VALUE ;
// Vorgaenger ( v )= s ;
Vorgaenger [ i ] = nodeNr ;
}
// Distanz ( s )=0;
Distanz [ nodeNr ] = 0;
int N u m b e r O f N o d e s I n S = 1;
// while S != V do {
while ( N u m b e r O f N o d e s I n S != g . nodeList . size ()) {
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
44
René Trumpp
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
Wasser für die Wüste
int u = findMin (S , Distanz , g );
// S = S +{ u };
S [ u ] = true ;
N u m b e r O f N o d e s I n S = 0;
for ( int i = 0; i < g . nodeList . size (); i ++) {
if ( S [ i ] == true )
N u m b e r O f N o d e s I n S ++;
}
// for each v from V \ S do {
for ( int v = 0; v < g . nodeList . size (); v ++) {
if ( S [ v ] == true || g . edgeMatrix [ u ][ v ] == NO_EDGE )
continue ;
// if Kante (u , v ) < Distanz ( v ) then {
if ( Distanz [ u ] + g . edgeMatrix [ u ][ v ] <= Distanz [ v ]) {
// Distanz ( v )= Kante (u , v );
Distanz [ v ] = Distanz [ u ] + g . edgeMatrix [ u ][ v ];
// Vorgaenger ( v )= u ;
Vorgaenger [ v ] = u ;
}
}
}
return Distanz ;
}
private int findMin ( boolean [] S , double [] Distanz , Graph g ) {
int minNode = 0;
double minDistanz = Double . MAX_VALUE ;
// Finde u from V \ S mit Distanz ( u )= min { Distanz ( v )| v from V \ S };
for ( int u = 0; u < g . nodeList . size (); u ++) {
if ( S [ u ] == true )
continue ;
if ( Distanz [ u ] < minDistanz ) {
minDistanz = Distanz [ u ];
minNode = u ;
}
}
return minNode ;
}
}
45
Literaturverzeichnis
[ProSte02]
Hans Jürgen Prömel und Angelika Steger
The Steiner Tree Problem
Vieweg 2002, ISBN 3-528-06762-4
[Wag03]
Christian Wagenknecht
Algorithmen und Komplexität
Fachbuchverlag Leipzig 2003, ISBN 3-446-22314-2
[AstBai02]
Alexander Asteroth und Christel Baier
Theoretische Informatik
Pearson Studium 2002, ISBN 3-8273-7033-7
[HoMoUl02]
John E. Hopcroft, Rajeev Motwani und Jeffrey D. Ullmann
Einführung in die Automatentheorie, Formale Sprachen und
Komplexitätstheorie (2. Auflage)
Pearson Studium 2002, ISBN 3-8273-7033-7
[CoLeRiSt01]
Thomas H. Corman, Charles E. Leiserson, Ronald L. Rivest und
Clifford Stein
Introduction to Algorithms
The MIT Press 2001, ISBN 0-2620-3293-7
[GrHoNiPr02] Clemens Gröpl, Stefan Hougardy, Till Nierhoff und
Hans Jürgen Prömel
Steiner Trees in Uniformly Quasi-Bipartite Graphs
Information Processing Letters 83, 2002
www.informatik.hu-berlin.de/∼proemel/publikationen/GHNP01d.pdf
46
Abbildungsverzeichnis
1.1 Das Königreich von König Algorimas . . .
1.2 Landkarte als Graph . . . . . . . . . . . .
1.3 Gerichtete und ungerichtete Kante . . . .
1.4 Gewichtete Kante bzw. gewichteter Graph
.
.
.
.
3
4
5
5
2.1
2.2
Beispiel für einen aufspannenden Baum . . . . . . . . . . . . . . .
Weiteres Beispiel für einen aufspannenden Baum . . . . . . . . . .
7
8
3.1
3.2
3.3
Lösung für König Algorimas erste Aufgabe bei 4 Punkten . . . . .
Lösung für 4 Punkte mit zusätzlichem Punkt in der Mitte . . . .
Landkarte mit zusätzlichen Verteilerstationen . . . . . . . . . . .
11
11
12
4.1
4.2
Eine mögliche Lösung des rechtwinkligen Steinerproblems . . . . .
Gitternetz mit Höhe und Breite 3 . . . . . . . . . . . . . . . . . .
14
15
5.1
Landkarte mit beliebigen zusätzlichen Verteilerstationen . . . . .
16
6.1
Transformiertes 3SAT-Problem . . . . . . . . . . . . . . . . . . .
19
7.1
7.2
7.3
7.4
7.5
7.6
7.7
Zyklische Durchmusterung . . . . .
Beispielgraph G . . . . . . . . . . .
Distanzgraph D zu G (Schritt 1) . .
M’ von D (Schritt 2) . . . . . . . .
Teilgraph G’ (Schritt 3) . . . . . .
M von G’ (Schritt 4) . . . . . . . .
Fertiger Steiner Baum B (Schritt 5)
.
.
.
.
.
.
.
25
27
27
27
27
27
27
9.1
Klassen Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
47
Herunterladen