Graph-Algorithmen - Universität Münster

Werbung
Westfälische Wilhelms-Universität Münster
Ausarbeitung
Graph-Algorithmen
im Rahmen des Seminars Parallele Programmierung
Bernd Kruthoff
Themensteller: Prof. Dr. Herbert Kuchen
Institut für Wirtschaftsinformatik
Praktische Informatik in der Wirtschaft
Inhaltsverzeichnis
Inhaltsverzeichnis
1
Einleitung............................................................................................................1
2
Graph-Algorithmen .............................................................................................2
2.1
2.2
2.3
Verbundene Komponenten................................................................................2
2.1.1
Überblick.............................................................................................2
2.1.2
Auf dem Parallelrechnermodell CREW PRAM...................................2
2.1.3
Auf weiteren Parallelrechnungsmodellen ............................................5
Kürzeste Pfade von einem Ausgangsknoten ausgehend ....................................5
2.2.1
Überblick.............................................................................................5
2.2.2
Sequentieller Algorithmus ...................................................................6
2.2.3
Parallelisierung des Algorithmus .........................................................8
Minimal spannender Baum .............................................................................12
2.3.1
Überblick...........................................................................................12
2.3.2
Sollins Algorithmus...........................................................................12
2.3.2.1 In sequentieller Ausführung.........................................................12
2.3.2.2 Parallelisierungsmöglichkeiten ....................................................15
2.3.2.3 Laufzeitverhalten .........................................................................15
2.3.3
Kruskals Algorithmus........................................................................16
2.3.3.1 In sequentieller Ausführung.........................................................16
2.3.3.2 Parallelisierungsmöglichkeiten ....................................................17
2.3.3.3 Laufzeitverhalten .........................................................................18
3
Zusammenfassung.............................................................................................18
4
Literaturverzeichnis..............................................................................................I
II
Kapitel 1: Einleitung
1 Einleitung
Viele Probleme lassen sich in verständlicher Form unter Benutzung von Objekten und
Verbindungen zwischen diesen beschreiben. Mathematisch können diese durch einen
Graphen abgebildet werden. Die so dargestellten Problemstellungen lassen sich mit
Hilfe von Graph-Algorithmen auf Computern lösen.
Um eine Steigerung der Rechengeschwindigkeit für eine schnellere Bearbeitung der
Algorithmen zu erreichen oder deren Berechenbarkeit gar erst zu ermöglichen, ist es oft
nötig, die sequentielle Von-Neumann-Architektur zu verlassen und Parallelrechner mit
mehreren Prozessoren zu nutzen. Dies wirkt sich auf die bisher zur Problemlösung
genutzten Algorithmen aus, die nun parallel bearbeitet werden sollen.
In dieser Ausarbeitung sollen in diesem Zusammenhang einige Graph-Algorithmen vorgestellt werden, die auch in Algorithmen für Parallelrechnermodelle umgewandelt werden können.
Zu Beginn wird die Suche nach verbundenen Komponenten auf Parallelrechnermodellen geschildert. Im Anschluss erfolgt die Beschreibung eines sequentiellen Algorithmus zum Auffinden von kürzesten Wegen von einem Ausgangsknoten zu allen
anderen Knoten und dessen Parallelisierungsmöglichkeiten. Abschließend werden zwei
Algorithmen zur Suche des minimal spannenden Baums eines Graphen in sequentieller
und paralleler Form behandelt.
Das Basiswissen für diese Ausarbeitung sollte aus den Grundstudiumsveranstaltungen
Quantitative Methoden I bzw. Informatik I oder Informatik II bekannt sein. Eine ausführlichere Einführung in die Grundbegriffe findet man in [OW96] oder auch in [SE02].
Für Erklärungen zu den an dieser Stelle verwendeten Architekturen von Parallelrechnermodelle wird auf die entsprechende Ausarbeitung im Rahmen dieses Seminars
oder auf [RR98] verwiesen.
Als Grundlage für diese Ausarbeitung dient [QU94].
1
Kapitel 2: Graph-Algorithmen
2 Graph-Algorithmen
2.1 Verbundene Komponenten
2.1.1 Überblick
Oftmals ist es bei Graphen von Interesse, ob und welche Knoten man von einem
beliebigen Knoten erreichen kann. Es existieren drei weit verbreitete Verfahren, um
verbundene Komponenten eines ungerichteten Graphen zu finden:
1. über Suchformen wie z. B. Tiefen- und Breitensuche.
2. über die Transitive Hülle, die über parallele Matrizenmultiplikation der
Adjazenzmatrix gefunden werden kann. Sequentiell kann diese mittels eines
Durchlaufs durch die Adjazenzmatrix über den bekannten Algorithmus von S.
Warshall ermittelt werden (siehe z. B. [SE02]).
3. über das Zusammenfügen von Knoten zu Komponenten, bis keine weiteren
Zusammenschlüsse durchgeführt werden können.
2.1.2 Auf dem Parallelrechnermodell CREW PRAM
Für das dritte Verfahren hat Hirschberg einen Algorithmus entwickelt, der auf der
Parallelisierung durch das sogenannte CREW PRAM Modell basiert und bei dem zu
Beginn alle Knoten eigenständige Komponenten sind. Jede Iteration läuft dabei in 3
Schritten ab:
1. Finden des Wurzelknotens einer Nachbarkomponente, der die geringste
Nummer aufweist. Wurzelknoten sind dabei die Knoten einer Komponente, die
wiederum die geringste Nummer haben.
2. Verbinden des Wurzelknotens der Komponente mit dem gefundenen
Wurzelknoten.
3. Bildung einer übergeordneten Komponente, so dass alle zugehörigen Knoten auf
den Wurzelknoten zeigen und diesen als Wert zugeordnet bekommen.
2
Kapitel 2: Graph-Algorithmen
Der Algorithmus endet, wenn keine weiteren Nachbarkomponenten gefunden werden
können. Dies soll anhand eines Beispiels verdeutlicht werden:
5
8
4
3
1
2
7
6
Abbildung 1: Beispielgraph für Hirschbergs Algorithmus
Knoten
Komponente
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
Tabelle 1: Komponentenzugehörigkeit vor der ersten Iteration
Die hervorgehobenen Kanten sind die in den jeweiligen Schritten interessierenden
Kanten. Es sind lediglich 2 Iterationen notwendig:
1. Iteration:
5
8
Schritt 1:
3
1
4
2
7
6
5
Schritt 2:
8
3
1
4
2
7
6
5
8
Schritt 3:
1
3
4
7
2
6
Abbildung 2: Schritte 1 bis 3 der ersten Iteration
3
Kapitel 2: Graph-Algorithmen
Knoten
Komponente
1
1
2
2
3
1
4
2
5
5
6
2
7
1
8
5
Tabelle 2: Komponentenzugehörigkeit nach der ersten Iteration
2. Iteration:
5
8
Schritt 1:
3
1
4
2
7
6
5
Schritt 2:
8
3
1
4
2
7
6
5
Schritt 3:
8
3
1
4
7
2
6
Abbildung 3: Schritte 1 bis 3 der zweiten Iteration
Knoten
Komponente
1
1
2
1
3
1
4
1
5
5
6
1
7
1
8
5
Tabelle 3: Komponentenzugehörigkeit nach der zweiten Iteration
Da sich in jedem Schritt die Anzahl der Komponenten mindestens um den Faktor 2
verringert, können alle n Knoten in
log n
Iterationen den entsprechenden
übergeordneten Komponenten zugewiesen werden. Die einzelnen Schritte je Iteration
4
Kapitel 2: Graph-Algorithmen
können dabei auf die Minimumbestimmung zurückgeführt werden, die eine
Zeitkomplexität von
(log n) besitzt (s. [HI76]), woraus sich ergibt, dass dieser Algo(log 2 n) Zeit be-
rithmus zum Auffinden aller verbundenen Komponenten insgesamt
nötigt. Dazu werden in Hirschbergs Version n2 Prozessoren benötigt, um eine Laufzeit
von
(log n) je Einzelschritt zu gewährleisten.
Allerdings lässt sich anhand des sogenannten Theorems von Brent feststellen, dass be
reits
( n n / log n ) Prozessoren für den Algorithmus ausreichen, da n / log n Pro
zessoren die Minimumsuche und die Zuweisung zu den Komponenten für n Knoten
weiterhin ohne Zeitverlust in
(log n) bewältigen können.
Eine weitere Verbesserung stellten Chin et al. vor. Diese kommt mit
( n n / log 2 n )
Prozessoren aus, da gemäß Beschreibung je Iteration nur ein repräsentativer Knoten der
zusammengelegten Komponenten benötigt wird und isolierte Komponenten von
weiteren Betrachtungen ausgeschlossen werden können.
2.1.3 Auf weiteren Parallelrechnungsmodellen
Nassimi und Sahni änderten Hirschbergs Algorithmus zur Nutzung auf einem 2dimensionalen mesh SIMD Modell ab (vgl. [NS80]). Sie stellten fest, dass bei n
Prozessoren, n = 2k Knoten und maximalem Vertexgrad d eine Zeitkomplexität von
( dn log n) erreicht werden kann. Unter Vertexgrad eines Knoten ist die Anzahl d der
von einem Knoten v abgehenden Kanten zu verstehen.
Ebenfalls auf Hirschbergs Algorithmus basierend entwickelten Miller und Stout einen
Algorithmus für eine Pyramidentopologie (s. [MS87]). Sie errechneten, dass das
Auffinden der verbundenen Komponenten in
( n ) möglich ist, wenn die
Adjazenzmatrix eines ungerichteten Graphen mit n Knoten in der Grundfläche eines
SIMD-P Rechner mit n2 Prozessoren gespeichert wird.
2.2 Kürzeste Pfade von einem Ausgangsknoten ausgehend
2.2.1 Überblick
Der von Moore 1959 entwickelte Algorithmus bestimmt die kürzesten Pfade von einem
gewählten Ausgangsknoten zu allen anderen Knoten eines gewichteten und gerichteten
5
Kapitel 2: Graph-Algorithmen
Graphen. Er wird hier zunächst in seiner sequentiellen Form vorgestellt, danach erfolgen Beschreibungen zur parallelen Abarbeitung des Algorithmus.
2.2.2 Sequentieller Algorithmus
Zur Betrachtung des Algorithmus wird nachstehender Graph genutzt:
B
4
A
D
1
2
1
3
C
E
Abbildung 4: Beispielgraph
Der Algorithmus benötigt ein Distanzarray, in dem die kürzesten Pfade zu den Knoten
geführt werden. Zu Beginn enthält dieses für jeden Pfad außer zum Ausgangsknoten das
Gewicht . Der kürzeste Pfad vom Ausgangsknoten zu sich selbst ist immer 0.
Des weiteren wird eine Queue der abzuarbeitenden Knoten geführt, in der ursprünglich
nur der Ausgangsknoten enthalten ist. Die Ausgangssituation sieht somit wie folgt aus:
Distanzarray
A
0
B
C
D
E
Queue
A
(a)
(b)
Tabelle 4: Distanzarray (a) und Queue (b) in der Ausgangssituation
Je Iteration geschieht nun Folgendes:
•
Zunächst wird der erste Knoten aus der Queue gelöscht.
•
Dann wird geprüft, ob die bisherigen Distanzen zu den Nachbarknoten
des gelöschten Knoten aktualisiert werden müssen. Dies ist der Fall,
wenn der bisherige Wert größer ist als die Distanz des gelöschten Knoten
zuzüglich der verbindenden Kante der beiden Knoten.
6
Kapitel 2: Graph-Algorithmen
•
Zuletzt werden alle vom gelöschten Knoten aus erreichbaren Knoten an
das Ende der Queue eingefügt, falls sie noch nicht in der Queue stehen
und sich ihr Wert im Distanzarray verringert hat.
Durch die Forderung, dass nur Knoten in die Queue aufgenommen werden, für die der
Wert im Distanzarray geringer wird, ist das Entstehen einer Endlosschleife aufgrund
eines Zyklus im Graphen nicht möglich, da sich bei der zweiten Betrachtung eines
Knoten des Zyklus die Distanz zu diesem durch die Nutzung des Zykluspfades nicht
verringert.
Der Algorithmus soll am Beispiel der ersten drei Iterationen verdeutlicht werden:
1. Iteration:
4
A
3
B
1
2
1
D
C
E
(a)
Distanzarray
A
0
B
4
C
1
D
E
Queue
B
C
(b)
(c)
Abbildung 5: Untersuchte Kanten (a), entstandenes Distanzarray (b) und Queue (c)
2. Iteration:
4
A
3
B
1
2
1
D
C
E
(a)
Distanzarray
A
0
B
4
C
1
D
7
E
Queue
C
D
(b)
(c)
Abbildung 6: Untersuchte Kante (a), entstandenes Distanzarray (b) und Queue (c)
3. Iteration:
B
4
A
D
2
1
1
C
E
(a)
Distanzarray
A
0
B
3
C
1
D
7
E
Queue
D
B
(b)
(c)
Abbildung 7: Untersuchte Kante (a), entstandenes Distanzarray (b) und Queue (c)
7
Kapitel 2: Graph-Algorithmen
Vor der letzten Iteration ergibt sich für das Distanzarray und die Queue folgendes Bild:
Distanzarray
A
0
B
3
C
1
D
6
E
7
Queue
E
(a)
(b)
Tabelle 5: Distanzarray (a) und Queue (b) vor der letzten Iteration
Als nächstes wird der Knoten E aus der Queue gelöscht. Da vom Knoten E kein
weiterer Knoten erreicht werden kann, ist keine Distanzüberprüfung nötig und es wird
kein Knoten in die Queue neu eingefügt. Nun ist die Queue leer und der Algorithmus
bricht ab.
2.2.3 Parallelisierung des Algorithmus
Es gibt zwei Möglichkeiten, eine parallele Bearbeitung dieses Algorithmus durch
mehrere Prozessoren zu erreichen. Zum einen kann die Untersuchung der abgehenden
Kanten des gelöschten Knotens und die evtl. nötige Aktualisierung des Distanzarray
parallelisiert werden. Alternativ kann die Bearbeitung der in der Queue befindlichen
Knoten parallel von mehreren asynchronen Prozessoren durchgeführt werden.
Die zweite Methode ist vorzuziehen, da die entstehenden Aufgaben für die einzelnen
Prozessoren größer sind und Möglichkeiten zur Parallelisierung bei der ersten Methode
aufgrund der begrenzten Anzahl der abgehenden Kanten eingeschränkt sind.
Der zugehörige Pseudocode des gewählten parallelen Ansatzes sieht wie folgt aus:
SHORTEST PATH
Global distance
n
halt
p
s
weight
{Element i enthält die Entfernung von s nach i}
{Anzahl Knoten im Graphen}
{Variable, die anzeigt, wann die
Prozesse halten können}
{Anzahl der Prozesse}
{Ausgangsknoten}
{Enthält Gewicht jeder Kante}
1. begin
2.
for all Pi, 1
i
p do {
for j = i to n step p do {
3.
4.
INITIALIZE(j)
5.
}
8
Kapitel 2: Graph-Algorithmen
6.
}
7.
enqueue s
8.
halt = false
9.
for all i, 1 i
p do {
10.
repeat SEARCH(i) until halt = true
11.
}
12. end
SEARCH (i)
parameter i
local
new_distance
u
v
{Prozessnummer}
{Entfernung nach v, wenn u
passiert wird}
{Untersuchter Knoten, von dem
Kanten abgehen}
{mit u über Kante verbundener
Knoten}
13. begin
14.
lock the queue
15.
if the queue is empty {
16.
waiting(i) = true
17.
if i = 1 {
18.
halt = waiting(2) and … and waiting (p)
19.
}
20.
unlock the queue
21.
}
22.
else {
23.
dequeue u
24.
waiting(i) = false
25.
unlock the queue
26.
for every edge {u,w} in the graph do {
27.
new_distance = distance(u) + weight ({u,w})
28.
lock (distance (v))
29.
if new_distance < distance(v) {
30.
distance(v) = new_distance
31.
unlock (distance(v))
32.
if v is not in the queue {
33.
lock the queue; enqueue v;
unlock the queue;
34.
}
35.
}
36.
else {
37.
unlock (distance(v))
38.
}
39.
}
40.
}
41. end
Durch die Nutzung mehrerer asynchroner Prozessoren entstehen Konflikte beim zeitgleichen Zugriff auf die Queue, der gleichzeitigen Aktualisierung des Distanzarrays und
beim Terminieren von Prozessen. Im Folgenden werden einige Erläuterungen zum
Pseudocode gegeben und es sollen Lösungsmöglichkeiten für diese Probleme dargestellt
9
Kapitel 2: Graph-Algorithmen
werden. Dabei wird davon ausgegangen, dass in der Regel ein Prozess auf einem
Prozessor läuft.
Der vorzuziehende parallele Algorithmus initialisiert in der Funktion INITIALIZE (j)
zunächst parallel das Distanzarray mittels der gestarteten Prozessoren (Zeile 2 bis 6).
Außerdem wird für jeden Prozessor die für ihn vorgesehene Variable waiting auf false
gesetzt. Sie wird für das Abbruchkriterium des Algorithmus benötigt.
Ein Prozessor sucht nun durch die Funktion SEARCH(i) in der Queue nach einem Knoten, den er bearbeiten kann. Da die Möglichkeit besteht, dass gleichzeitig mehrere Prozessoren für ihren laufenden Prozess auf die Queue zugreifen wollen, muss ein Sperrmechanismus eingebaut werden, so dass exklusiver Zugriff erreicht wird (Zeile 14).
Sobald der jeweilige Prozessor keinen weiteren abzuarbeitenden Knoten in der Queue
findet, setzt er seine Variable waiting auf true und gibt die Queue wieder frei (Zeilen 15,
16 und 20). Wenn Prozessor 1 warten muss, kontrolliert er vor der Freigabe noch, ob
die zusätzlich benötigte Variable halt auf true gesetzt werden muss und somit das Abbruchkriterium erreicht wird (Zeile 17 bis 19). Dies ist der Fall, wenn die Variable
waiting aller anderen Prozessoren ebenfalls den Wert true hat. Erst dann können alle
Prozessoren stoppen. Durch dieses Vorgehen kann verhindert werden, dass ein Prozessor sofort terminiert, wenn er erkennt, dass die Queue (momentan) leer ist.
Stattdessen überprüft er die Queue solange, bis die Variable halt auf true gesetzt ist.
Findet der jeweilige Prozessor aber einen Knoten in der Queue, muss dieser aus der
Queue gelöscht werden und die Variable waiting des Prozessors auf false gesetzt
werden, da der Prozessor nun mit der Bearbeitung dieses Knotens startet, und die Sperre
der Queue aufgehoben werden (Zeile 23 bis 25). Bei der Aktualisierung des
Distanzarray muss gewährleistet werden, dass der entsprechende Prozessor momentan
alleinigen Zugriff auf das neu zu belegende Feld des Distanzarrays besitzt, denn sonst
kann es zu einer Art Rennen zwischen den Prozessoren kommen. Dies wird wieder über
eine exklusive Sperrung für diesen Prozessor erreicht, indem die Variable distance(v)
für die Dauer der Überprüfung und Aktualisierung gesperrt wird (Zeilen 28 ff.). Falls
der zu erreichende Knoten in die Queue eingefügt werden muss, ist wieder ein
exklusiver Zugriff auf diese durch den jeweiligen Prozessor nötig (Zeile 32 bis 34).
Insbesondere das Sperren der Queue kann allerdings die Geschwindigkeitsbeschleunigung des parallelen Algorithmus erheblich beeinträchtigen. Um diesen Konflikt zwischen den Prozessoren zu umgehen, eignet sich die Datenstruktur des Linked Array.
10
Kapitel 2: Graph-Algorithmen
Zur parallelen Bearbeitung sollten dazu zwei Arrays genutzt werden: Ein Array für die
in der laufenden Iteration zu untersuchende Queue und ein Array, in dem die Knoten
aufgeführt werden, die während dieser Iteration an die Queue angehangen werden, und
das somit die Queue für die nächste Iteration bildet. Dies kann auch mit nur einem
Array durch Aufteilung abgebildet werden.
Der Grundgedanke des Linked Array ist dabei einerseits, dass die p Prozessoren jeweils
den p-ten Knoten des einen Arrays untersuchen und löschen. Andererseits trägt jeder
Prozessor zu untersuchende Knoten, die er je Iteration findet, nur in einen für ihn abgegrenzten Bereich des anderen Arrays ein. An diese Einträge werden dann nach jeder
Iteration jeweils p Verweise auf den nächsten Bereich angehängt. So können in der
nächsten Iteration einfach die Rollen zwischen den Arrays getauscht werden. Die
Prozessoren können dann das zu untersuchende Array mit Hilfe der Verweise leicht
durchsuchen. Wenn ein Prozessor einen Verweis findet, der außerhalb des zu bearbeitenden Arrays liegt, gibt es keine weiteren abzuarbeitenden Knoten für ihn.
Im Folgenden sind beispielhaft für p=4 Prozessoren die beiden Arrays dargestellt. Xij
bezeichnet dabei den j-ten eingefügten Knoten des Prozessors i:
P1
P2
P3
P4
P1
X11
X12
X13
X14
X15
P2
P3
P4
P1
...
X21
X22
X23
...
Abbildung 8: Array der Queue mit den zu bearbeitenden Knoten und Verweisen. Links vom
Trennstrich hat Prozessor 1 in der vorherigen Iteration Knoten und Verweise eingefügt hat.
X11
...
Bereich für P1
X21
...
Bereich für P2
X31
...
Bereich für P3
X41
...
Bereich für P4
Abbildung 9: Array mit Bereichen für jeden Prozessor zum Einfügen in die Queue
Durch dieses Vorgehen werden Zugriffskonflikte auf die Arrays unter den Prozessoren
vermieden, da es je ein Array für die zu bearbeitenden (und somit zu löschenden)
Knoten und für die einzufügenden Knoten gibt. Außerdem wird eine gleichmäßige
Lastverteilung zwischen den Prozessoren erreicht, was sich durch einen nahezu gleichen
Anteil von zu bearbeitenden Knoten für jeden Prozessor je Iteration ausdrückt.
11
Kapitel 2: Graph-Algorithmen
Mit Hilfe dieser Datenstruktur lässt sich die erreichbare Beschleunigung der Parallelisierung optimieren, obwohl Zeit für das Nachvollziehen der Verweise und die Synchronisation der Prozessoren am Ende jeder Iteration verloren geht.
Falls es nicht nur einen Prozessor gibt, der die Einfügeoperationen vornimmt, müssen
die Arrays erheblich größer dimensioniert werden als hätte man einen Prozessor für
diese Aufgabe, da Knoten dann von verschiedenen Prozessoren mehrmals in das Array
eingefügt werden können. Dadurch kann außerdem wieder das Problem des gleichzeitigen Zugriff auf das Distanzarray in Form der Variable distance(v) auftreten, was
wieder einen Sperrmechanismus nötig macht.
Die Laufzeit hängt bei Moores Algorithmus vom Aufbau des Graphen und der Knotenfolge in der Queue und der damit verbundenen Abarbeitungsreihenfolge der Knoten ab.
Die Anzahl der nutzbaren Prozessoren ist durch die Anzahl der Knoten des Graphen
beschränkt, da sich maximal n – 1 Knoten in der Queue befinden können.
2.3 Minimal spannender Baum
2.3.1 Überblick
Ein minimal spannender Baum ist ein Baum eines verbundenen Graphen, der alle seine
Knoten mit minimaler Gesamtlänge/minimalem Gesamtgewicht der Kanten verbindet.
Die Algorithmen zum Auffinden dieser Bäume finden häufig Anwendung, z. B. bei der
Suche nach minimalen Daten- oder Telekommunikationsnetzen. Im folgenden werden
zwei bekannte, sequentielle Algorithmen für gewichtete und ungerichtete Graphen und
die Möglichkeiten zu deren paralleler Bearbeitung vorgestellt.
2.3.2 Sollins Algorithmus
2.3.2.1 In sequentieller Ausführung
Der Algorithmus von Sollin hat die gleiche Vorgehensweise wie der zuvor beschriebene
Algorithmus von Hirschberg (Kap. 3.1.2). Er startet ebenfalls mit einem Wald von
Teilbäumen, die jeweils nur einen einzelnen Knoten umfassen. Anstatt der Komponente
mit dem geringsten Wert wird in jeder Iteration die Kante mit geringsten Gewicht zu
einem anderen Teilbaum gesucht und die beiden Teilbäume über diese Kante
verbunden, wenn kein Zyklus entsteht. Ein minimal spannender Baum muss zyklenfrei
sein, denn sonst ist eine Kante überflüssig, um alle Knoten erreichen zu können.
12
Kapitel 2: Graph-Algorithmen
Anhand des folgenden Beispielgraphen soll die Vorgehensweise verdeutlicht werden:
4
A
5
2
B
C
6
G
1
1
5
3
H
D
1
2
I
3
7
E
F
4
Abbildung 10: Beispielgraph für Sollins Algorithmus
Die Wahl der Kanten für die einzelnen Knoten in der ersten Iteration zeigt die nächste
Abbildung:
4
A
5
2
B
C
6
3
1
5
G
1
H
D
1
2
I
3
7
E
F
4
Abbildung 11: Kantenwahl (hervorgehoben) in der 1. Iteration
Wenn der Algorithmus für einen Teilbaum zwei abgehende Kanten mit dem gleichen
Gewicht findet, ist die Auswahl der Kante laufzeitabhängig und wird auch durch die
Datenstruktur der Kanten beeinflusst. Daraus resultiert, dass der Algorithmus nicht
deterministisch ist. Im Beispielgraph ist diese Situation für den Knoten G gegeben.
Da kein Zyklus entsteht, wenn die gefundenen Kanten in die Menge T der benötigen
Kanten eingefügt werden, können die Teilbäume zu jeweils einem Teilbaum über die
gefundenen Kanten verbunden werden. Es existieren am Ende der ersten Iteration somit
4 Teilbäume und die Menge T enthält 5 Kanten.
In der zweiten Iteration wird die Menge T um die Kanten {A,G} und entweder {C,H}
bzw. {D,I} erweitert.
Die beiden übriggebliebenen Teilbäume werden in der dritten und letzten Iteration über
die Kante {B,C} oder {C,G} verbunden.
13
Kapitel 2: Graph-Algorithmen
Daraus kann sich folgender minimal spannender Baum ergeben:
4
A
B
C
G
1
1
5
3
H
D
1
2
I
3
F
E
Abbildung 12: Minimal spannender Baum des Beispielgraphen nach Sollin
Der zugehörige parallele Pseudocode unterscheidet sich von der sequentiellen Version
lediglich durch zwei zusätzliche Zeilen. Diese sind nötig, damit zwei Prozessoren nicht
gleichzeitig die gleichen Teilbäume verbinden. Sie sind in rot hervorgehoben:
MINIMUM SPANNING TREE
1. begin
2. n
{Anzahl Knoten im Graphen}
3. T
{Menge der für den minimal aufspannenden Baum
nötigen Kanten}
4.
for i = 1 to n do {
5.
Knoten i gehört zunächst zum Teilbaum i
6.
}
7.
T = ∅
8.
while |T| < n – 1 do { // Abbruchkriterium
for every tree i do {
9.
10.
closest(i) =
11.
}
12.
for every edge {v,w} do {
13.
if FIND (v) != FIND (w) {
14.
if weight ({v,w}) < closest(FIND(v)){
15.
closest (FIND (v)) = weight ({v,w})
16.
edge (FIND (v)) = {v,w}
17.
}
18.
}
19.
}
20.
for every tree i do {
21.
{v,w} = edge i
22.
lock v, lock u
23.
if FIND (v) != FIND (w){
24.
T = T ∪ {v,w}
25.
UNION (v,w)
26.
}
27.
unlock v, unlock u
28.
}
29.
}
14
Kapitel 2: Graph-Algorithmen
30. end
UNION und FIND sind effiziente Funktionen, um zum einen zwei Mengen zu einer
zusammenzufassen bzw. den Namen der Menge wiederzugeben, in der sich ein Knoten
befindet. Näheres zu deren Ablauf und Laufzeitverhalten kann u.a. in [AH74] und in
[HU73] nachgelesen werden.
2.3.2.2 Parallelisierungsmöglichkeiten
Die Iterationen, die im Pseudocode durch die gesamte while-Schleife abgebildet
werden, können nicht parallelisiert werden, da jede zunächst abgeschlossen sein muss,
bevor die nächste starten kann.
Bei den beiden for-Schleifen zur vorbereitenden Initialisierung der kürzesten Kanten
mit dem Gewicht
(Zeile 9 bis 11) und zur Suche der Kanten mit dem geringsten
Gewicht (Zeile 12 bis 19) kann eine Parallelisierung durch Aufteilen der zu
bearbeitenden Knoten auf die Prozessoren erfolgen.
Für das Verbinden der Teilbäume (Zeile 20 bis 28) muss der parallele Algorithmus wie
schon erwähnt erweitert werden. Der Grund dafür ist, dass zwei Prozessoren nicht
gleichzeitig zwei Teilbäume verbinden dürfen, denn dann würde eine überflüssige
Kante in die Menge T der benötigten Kanten aufgenommen. Deshalb wird ein
Sperrmechanismus eingebaut (Zeile 22 und 27), der für exklusiven Zugriff auf zwei
Teilbäume sorgt, die ein Prozessor zusammenfügen möchte.
In [OW96] ist eine parallele Version für Sollins Algorithmus angegeben, bei der für
jeden der n Knoten ein Prozessor zur Verfügung steht. Im ersten Schritt suchen die
Prozessoren für die Ihnen zugeteilten Knoten und nicht für Teilbäume die Kante mit
dem geringsten Gewicht. Dann wird für jeden Teilbaum die Kante mit dem geringsten
Gewicht ausgewählt, in dem die Prozessoren der Knoten, die bereits zu einem Teilbaum
gehören, ihre jeweils gewählte Kante vergleichen. Die übriggebliebenen Prozessoren
enthalten dann die Kanten über die nun neue Teilbäume gebildet werden müssen.
2.3.2.3 Laufzeitverhalten
Die Zeitkomplexität dieses Algorithmus hängt bei Parallelisierung der Schleifen von der
Anzahl der Knoten n ab, da diese in jeder Iteration betrachtet werden, und von der
Anzahl der zur Verfügung stehenden Prozessoren p.
In jedem Durchlauf verringert sich die Anzahl der Teilbäume um mindestens den Faktor
2, da jeder Teilbaum mit mindestens einem weiteren Teilbaum verbunden wird.
15
Kapitel 2: Graph-Algorithmen
Dadurch sind höchstens log n Durchläufe notwendig. Dies muss mit der Komplexität
einer Iteration multipliziert werden.
Die in den Iterationen genutzten UNION und FIND-Operationen können mit einer sehr
langsam wachsenden Zeitkomplexität von
(log* n) vereinfachend als Konstanten
angesehen werden.
Die Initialisierung in den Zeilen 9 bis 11 hat eine Komplexität von n / p , da die
Teilbäume auf die p Prozessoren vorverteilt werden können. Dazu muss für die Suche
der Kante mit dem geringsten Gewicht bei allen Knoten in der zweiten for-Schleife
n2 / p addiert werden, weil von n Knoten n–1 Kanten abgehen können und somit n2
Untersuchungen auf p Prozessoren aufgeteilt werden müssen. Dazu kommt der Wert
n / p * p für die letzte for-Schleife, da die n Knoten/Teilbäume zum Verbinden zwar auf
p Prozessoren vorverteilt werden können, aber im worst case ein Prozessor auf alle
anderen Prozessoren warten, bis er einen Teilbaum sperren kann. Da die Datenstruktur
der Knoten bei der Auftragsvergabe auf die Prozessoren gesperrt werden muss, hat jede
von den drei for-Schleifen noch einen zusätzlichen Zeitaufwand von p.
Daraus ergibt sich folgende Zeitkomplexität:
( log n ) * ( (n / p + p) +
=
( log n ) *
( n2 / p + p ) +
( n2 / p + n / p + n + 3 p ) =
( n / p * p + p))
( log n ( n2 / p + n / p + n + p))
Eine gute Beschleunigung kann erreicht werden, wenn p < < n.
Bei dem in [OW96] aufgezeigten Algorithmus sind ebenfalls log n Iterationen not
wendig, allerdings können die Teilschritte jeweils in
eine Zeitkomplexität von
( n) durchgeführt werden, so dass
(n log n) erreicht wird, unter der Voraussetzung, dass p = n
Prozessoren zur Verfügung stehen.
2.3.3 Kruskals Algorithmus
2.3.3.1 In sequentieller Ausführung
Dieser Algorithmus ist seit spätestens 1956 bekannt und wird allgemein J.B. Kruskal
zugesprochen, der ihn in [KR56] erstmals publiziert.
Zunächst sind wie bei Sollins Algorithmus alle Knoten für sich Teilbäume eines
Waldes. Es wird zu Beginn die Kante mit dem geringsten Gewicht aufgenommen und
16
Kapitel 2: Graph-Algorithmen
die entsprechenden Teilbäume verbunden. Dann folgt schrittweise die Kante, die von
den übriggebliebenen Kanten wiederum das geringste Gewicht besitzt und die keinen
Zyklus entstehen lässt, bis alle Knoten des Graphen in einem Baum enthalten sind.
Diese Verfahrensweise wird nun tabellarisch anhand des Beispielgraphen aus Abb. 7
aufgezeigt:
Kante
Gewicht
Entsteht
1
1
1
2
2
3
3
4
4
5
Zyklus?
Nein
Nein
Nein
Nein
Ja
Nein
Nein
Nein
Ja
Nein
{A,F}
{C,D}
{H,I}
{C,H}
{D,I}
{A,G}
{G,E}
{A,B}
{F,E}
{B,C}
Aufnehmen? Alle Knoten in
einem Baum?
Ja
Nein
Ja
Nein
Ja
Nein
Ja
Nein
Nein
Nein
Ja
Nein
Ja
Nein
Ja
Nein
Nein
Nein
Ja
Ja
Tabelle 6: Ermittlung der nötigen Kanten
Nach Aufnahme der Kante {B,C} ist folgender minimal spannende Baum entstanden:
5
4
A
B
2
C
H
1
1
3
G
1
D
I
3
F
E
Abbildung 13: Minimal spannender Baum des Beispielgraphen nach Kruskal
Die Sortierung der Kanten bei gleichem Gewicht hängt wieder von der Datenstruktur
ab, so dass dieser Algorithmus auch nicht deterministisch ist und ebenfalls der minimal
spannende Baum nach Sollin hätte entstehen können.
2.3.3.2 Parallelisierungsmöglichkeiten
Zur Parallelisierung dieses Algorithmus wird ein Heap eingesetzt, der die Kanten in der
gewünschten aufsteigenden Reihenfolge in die Wurzel schiebt, wo sie entnommen
17
Kapitel 2: Graph-Algorithmen
werden. Der Aufbau eines Heap wird hier als bekannt vorgesetzt, da er Bestandteil von
Grundstudiumsveranstaltungen ist.
Die einzelnen Ebenen des Heap werden jeweils von einem Prozessor bearbeitet. Ein
weiterer Prozessor wird benötigt, um die Kante, die sich aktuell in der Wurzel befindet,
zu entnehmen, zu prüfen, ob sie für den minimal spannenden Baum notwendig ist, und
die entsprechenden Teilbäume gegebenenfalls zu verbinden. Entsteht auf einer Ebene
durch die Entnahme eines Knoten ein freier Platz, wird dieser mit dem passenden
Nachfolger der darunter liegenden Ebene gefüllt und der Prozessor für diese Ebene
muss seinerseits nun aktiv werden. Wenn auf der unteren Ebene leere Blattknoten entstehen, werden diese mit dem Wert ∞ gefüllt. Sobald die Wurzel diesen Wert annimmt,
endet der parallele Algorithmus und ein minimal spannender Baum ist gefunden.
2.3.3.3 Laufzeitverhalten
Bei der Parallelisierung von Kruskals Algorithmus hängt die Zeitkomplexität von der
Anzahl der Kanten m ab, die der Heap enthält da diese in jeder Iteration entnommen
werden. Der parallele Neuaufbau des Heap eines verbundenen und gerichteten Graphen
und die damit verbundene erneute Bereitstellung einer entnehmbaren Kante kann in
konstanter Zeit von einem UMA Multiprozessor mit log m Prozessoren durchgeführt
werden. Dabei kann jeder Prozessor, der sich um eine Ebene kümmert, diese ebenfalls
als Heap verwalten. Unter Zuhilfenahme der Funktionen UNION und FIND kann auch
die Entnahme der Wurzel, deren Überprüfung und die Verbindung der Teilbäume von
dem zuständigen Prozessor in (nahezu) konstanter Zeit durchgeführt werden.
Daraus resultiert eine Zeitkomplexität von
( m) bei
log m
Prozessoren für den
parallelisierten Algorithmus von Kruskal.
3 Zusammenfassung
Aus dem weiten Feld von Graphen-Algorithmen wurde in dieser Arbeit zunächst der
Algorithmus zur Erkennung der verbundenen Komponenten auf dem CREW PRAM
Modell in der Version von Hirschberg und dessen Verbesserungsmöglichkeiten vorgestellt. Zur Lösung dieses Problems nutzt er das Verfahren des Zusammenfügen von
Knoten zu Komponenten. Entsprechende Algorithmen für weitere Parallelrechnermodelle basieren auf diesem.
18
Kapitel 3: Zusammenfassung
Als nächstes wurde der Algorithmus von Moore zur Suche nach den kürzesten Pfaden
von einem Ausgangsknoten zu allen anderen Knoten aufgezeigt. Dieser nutzt ein Distanzarray und eine Queue. Weiterhin wurde die Möglichkeit der Parallelisierung in Bezug auf die Schleifen des Algorithmus dargestellt. Diese ist auf die Nutzung von Sperrmechanismen angewiesen. Eine effizientere parallele Bearbeitung bietet hier die Nutzung der Datenstruktur Linked Array.
Abschließend wurden noch zwei bekannte Algorithmen zum Auffinden eines minimal
spannenden Baumes und deren Parallelisierung erläutert. Dabei handelt es sich einerseits um den Algorithmus von Sollin, der ein ähnliches Vorgehen wie Hirschbergs Algorithmus anwendet. Eine Parallelisierung ist hier ebenfalls innerhalb der Schleifen
unter einfachem Hinzufügen eines Sperrmechanismus möglich. Eine gute Beschleunigung wird dabei erreicht, wenn die Anzahl der Prozessoren erheblich kleiner ist als
die Anzahl der Knoten.
Eine etwas abgewandelte Version für den Fall, dass die Anzahl der Prozessoren mit der
Anzahl der Knoten übereinstimmt, wurde angesprochen.
Außerdem wurde der Algorithmus von Kruskal beschrieben. Er sucht sich nacheinander
die jeweils kürzeste Kante und fügt sie in die Menge der benötigten Kante ein, wenn dadurch kein Zyklus im Graphen entsteht. Dessen parallele Abarbeitung erfolgt durch die
Nutzung eines Heap und ergibt eine von der Anzahl m der Kanten abhängige
Zeitkomplexität von
( m) .
Für die weitere Entwicklung ist eine Untersuchung der vorgestellten Algorithmen für
speziellere bzw. andere Parallelrechnermodelle denkbar.
19
Literaturverzeichnis
4 Literaturverzeichnis
[AH74]
A.V. Aho, J.E. Hopcraft, J.D. Ullman: The Design and Analysis of
Computer Algorithms, Addison-Wesley, 1974.
[AK89]
S. G. Akl: The Design and Analysis of Parallel Algorithms, Prentice
Hall, 1989.
[CL81]
F.Y. Chin, J. Lam, I.N. Chen: Optimal parallel algorithms for the
connected component problem, Proceedings of the 1981 International
Conference on Parallel Processing, IEEE, New York, Aug., S. 170-175,
1981.
[CL82]
F.Y. Chin, J. Lam, I.N. Chen: Efficient parallel algorithms for some
graph problems, Communications ot the ACM, vol. 25, no. 9, Sept., S.
659–665, 1982.
[GW88]
A. Gibbsons, W. Rytter: Efficient Parallel Algorithm, Cambridge
University Press, 1988.
[HI76]
D.S. Hirschberg: Parallel algorithms for the transitive closure and the
connected component problem, Proceedings of the 8th Annual ACM
Symposium on the Theory of Computing, ACM, New York, May, S. 5557, 1976.
[HU73]
J.E. Hopcraft, J.D. Ullman: Set-merging algorithms, SIAM Journal on
Computing, vol. 2, S. 294-303, 1973.
[KR56]
J.B. Kruskal: On the shortest subtree of a graph and the traveling
salesman problem, Proceedings of the American Mathematical Society,
vol.7, Feb., S. 48-50, 1956.
[MS87]
R. Miller, Q.F. Stout: Data movement techniques for the pyramid
computer, SIAM Journal on Computing, vol. 16, no. 1, Feb., S. 38-60,
1987.
[NS80]
D. Nassimi, S. Sahni: Finding connected components and connected
ones an a mesh-connected parallel computer, SIAM Journal on
Computing, vol. 9, no. 4, Nov., S. 744-757, 1980.
1
Literaturverzeichnis
[OW96]
T. Ottmann, P. Widmayer: Algorithmen und Datenstrukturen, 3. Aufl.,
Spektrum Lehrbuch, 1996.
[QU94]
M. J. Quinn: Parallel Computing Theory and Practise, 2nd ed., Kapitel
12.2, 12.4-12.6, McGraw-Hill, 1994.
[RR98]
Rauber, T. und G. Rünger: Parallele und verteilte Programierung.,
Kapitel 2.3, 2.4, 2.7, 2.8. Springer 1998.
[SE02]
R. Sedgewick: Algorithmen, 2. Aufl., Pearson Studium, 2002.
II
Herunterladen