Graphen

Werbung
151
6. Graphen
• viele praktische (Optimierungs-)Probleme
sind als graphentheoretische Probleme formulierbar
• z.B. in Produktionsplanung, Personaleinsatzplanung, . . .
6.1 Grundlagen
• gerichteter, ungerichteter und gewichteter Graph
152
Gerichteter Graph
Definition: gerichteter Graph G = (V, E) mit:
V = {v0, . . . , vn−1}
Knotenmenge
E ⊂V ×V
Kantenmenge
(im folgenden: m =| E |)
• Graphen werden meist graphisch dargestellt
Beispiel:
G1 = ({v0, v1, v2, v3}, {(v0, v2), (v1, v2), (v2, v1), (v2, v3)})
v0
v1
v2
v3
153
Ungerichteter Graph
Definition: ungerichteter Graph G = (V, E) mit:
V = {v0, . . . , vn−1}
Knotenmenge
E ⊂ {{v, v 0} | v, v 0 ∈ V }
Kantenmenge
Beispiel:
G2 = ({v0, v1, v2, v3}, {{v0, v2}, {v1, v2}, {v2, v3}})
v0
v1
v2
v3
• im folgenden werden gerichtete Graphen betrachtet,
sofern nicht explizit anders hervorgehoben
154
Gewichteter Graph
Definition: gewichteter Graph G = (V, E, β) mit:
Knotenmenge V
Kantenmenge E
β : E−→IR
(Kanten-)Gewichtsfunktion
Beispiel:
G3 = ({v0, v1, v2, v3}, {(v0, v2), (v1, v2), (v2, v1), (v2, v3)}, β)
mit β(v0, v2) = 2, β(v1, v2) = 5, β(v2, v1) = 3, β(v2, v3) = 2
v0
2
v2
v1
5
3
2
v3
155
6.2 Datenstrukturen für Graphen
6.2.1 Adjazenzmatrix
sei G = (V, E) ein Graph
Definition: Adjazenzmatrix AG ist n × n-Matrix mit
(
1, falls (vi, vj ) ∈ E f ür 0 ≤ i, j < n (bzw. {vi, vj } ∈ E)
ai,j =
0, sonst
Beispiel:
v0
v1

AG 1
G1 :
• Platzbedarf: O(n2)
v2
v3
0
 0
= 
 0
0
0
0
1
0
1
1
0
0

0
0 

1 
0
156
6.2.2 Adjazenzlisten
• Array (oder sonstige Kollektion) von Knoten
• für jeden Knoten:
Liste mit Verweisen auf Nachfolgeknoten
Beispiel:
0
v0
G1 :
v3
• Kantengewichte leicht ergänzbar
• Platzbedarf: O(n + m)
2
3
v1
-
v2
1
2
-
2
1
-
3
157
6.2.3 Distanzmatrix
sei G = (V, E, β) ein gewichteter Graph
Definition: Distanzmatrix DG ist n × n-Matrix mit
(
β(vi, vj ), falls (vi, vj ) ∈ E f ür 0 ≤ i, j < n (bzw. {vi, vj } ∈ E)
di,j =
∞,
sonst
Beispiel:
v0
2
G3 :
v2
• Platzbedarf: O(n2)
v1

5
DG3
3
2
v3

∞ ∞ 2 ∞
 ∞ ∞ 5 ∞ 

= 
 ∞ 3 ∞ 2 
∞ ∞ ∞ ∞
158
6.3 Transitive Hülle
• gegeben: G = (V, E)
• gesucht: (reflexive,) transitive Hülle G∗ = (V, E ∗) mit:
? (v, v) ∈ E ∗
∀v ∈ V
? (v, v 0) ∈ E ∗, (v 0, v 00) ∈ E ⇒ (v, v 00) ∈ E ∗
∀v, v 0, v 00 ∈ V
• sonst keine Kanten in E ∗
Beispiel:
G1 = (V, E):
v0
v1
v0
v1
v2
v3
v2
v3
G∗1 = (V, E ∗):
159
Transitive Hülle in Java
• gegeben: Adjazenzmatrix AG von G = (V, E)
public static void huelle(byte[][] A){
int n = A.length;
for(int i=0; i<n; i++)
A[i][i] = 1;
for(int j=0; j<n; j++)
for(int i=0; i<n; i++)
if (A[i][j] == 1)
for(int k=0; k<n; k++)
if (A[j][k] == 1)
A[i][k] = 1;}
2
∗
• Aufwand: trth
(n,
m)
∈
O(n
+
|
E
| ·n))
W
160
6.4 Durchlaufen von Graphen
6.4.1 Tiefensuche
• an jedem Knoten:
? zuerst die Nachfolger rekursiv betrachten
? dann die “Geschwister”
Beispiel:
G4:
v0
v1
v4
v2
v3
v5
Durchlaufreihenfolge: v0, v2, v1, v4, v3, v5
s
• Aufwand: tdf
W (n, m) ∈ O(n + m) (bei Adjazenzlisten)
161
6.4.2 Breitensuche
• alle Knoten mit Abstand i vom Ausgangsknoten
werden vor denen mit Abstand i + 1 besucht (i = 0, 1, 2, . . .)
• hierzu: Knotenqueue (FIFO)
Beispiel:
G4:
v0
v1
v4
v2
v3
v5
Durchlaufreihenfolge: v0, v2, v1, v3, v4, v5
s
• Aufwand: tbf
W (n, m) ∈ O(n + m) (bei Adjazenzlisten)
162
6.5 Kürzeste Wege
6.5.1 Kürzeste Wege von einem Ausgangsknoten
• sei G = (V, E, β) mit β : E−→IR+
Algorithmus von Dijkstra (grober Pseudocode)
drei Knotenmengen:
F (fertig), R (Rand), U (unerreicht)
anfangs:
F = {v0};
R = Nachbarn(v0);
U =V −F −R
bis F == V
(*) bestimme v ∈ R mit d(v0, v) ≤ d(v0, v 0) ∀ v 0 ∈ R
F = F ∪ {v}
R = R ∪ (Nachbarn(v) − F ) − {v}
U = U −R
verschiedene Implementierungen von (*) möglich:
1) durchlaufe alle Knoten: → Aufwand O(n2) (Original)
2) mit Knoten-Heap: → Aufwand O(m · log n)
3) mit Knoten-Fibonacci-Heap (s. Ottmann/Widm.): Aufwand O(m + n · log n)
163
Beispiel: Algorithmus von Dijkstra
v0
0
3
v2
2
6
3
v2
2
6
v1
5
2
3
v2
2
3
v4
3
4
5
1
v4
2
v3
7
3
4
6
v1
5
2
2
6
3
v2
2
5
1
v4
2
v3
2
v0
0
1
3
v2
2
v1
5
2
5
2
v3
2
v0
0
v4
6
v0
0
1
2
v1
5
2
6
v3
2
v0
0
v1
5
2
2
6
3
4
5
1
v4
2
v3
3
4
6
164
6.5.2 Alle Paare kürzester Wege
Ansatz 1: n-mal Dijkstra-Algorithmus
• Aufwand: O(n3) bzw. O(n · m · log n) (mit Heap)
Ansatz 2: (+,min)-Multiplikationen
geg.: Distanzmatrix D
M = D;
for(int i=0; i <= log2 n; i++)
M = M ⊕ M;
wobei ⊕: (+,min)-Matrixmultiplikation, d.h. min statt +,
• Aufwand: O(n3 · log n)
• verbesserbar auf: O(n2.81 · log n) mit Strassen-Verfahren
+ statt ·
165
6.6 Minimaler Spannender Baum
• gegeben: ungerichteter, gewichteter Graph G = (V, E, β)
0
0
0
0
• gesucht: Baum B = (V, E , β ) mit E ⊂ E, β = β |E 0 und
P
β(e) minimal
e∈E 0
Algorithmus von Kruskal (Pseudocode)
E0 = ∅
while B noch kein Baum do
wähle e ∈ E − E 0 mit kleinstem β(e)
und so dass B = (V, E 0 ∪ {e}) ‘‘kreisfrei’’
E 0 = E 0 ∪ {e}
• Aufwand: O(m · log n) mit “union-find-Struktur” (s. z.B. Ottmann/Widmayer)
166
Beispiel: Algorithmus von Kruskal
6
v0
v1
v0
1
2
v4
2
3
2
6
v0
v3
v1
2
1
3
v2
2
6
v0
1
v4
2
3
v3
v1
3
1
2
2
2
v4
2
3
v2
v1
2
2
v2
6
2
v3
3
2
v4
2
3
v2
2
v3
3
167
Algorithmus von Jarnı́k/Prim/Dijkstra
• analog zu Dijkstra-Algorithmus (s.o)
• jedoch wird für jeden Randknoten der Abstand zum fertigen Baum vermerkt
(nicht zum Anfangsknoten)
Beispiel:
6
v0
v1
6
v0
1
2
v4
2
3
v2
2
6
v0
v0
1
v3
v1
v4
2
3
3
v2
2
2
6
v0
1
v4
2
3
2
v3
3
1
2
v3
v1
3
2
v3
2
2
1
1
v4
3
• Aufwand: O(m + n log n) (mit Fibonacci-Heap)
v4
2
v2
2
2
v2
v1
3
3
2
2
6
2
2
2
v2
v1
3
2
2
2
6
2
v3
3
3
168
6.7 Maximaler Fluss
• gegeben: gewichteter Graph G = (V, E, β) mit
β : E−→IR+
Kantenkapazität
q∈V
Quelle
s∈V
Senke
• gesucht: maximaler Fluss f : E−→IR+ von q nach s
P
P
d.h. max (
f (q, v) −
f (v 0, q)) mit:
f :E−→IR+ (q,v)∈E
(v 0 ,q)∈E
* f (e) ≤ β(e) ∀e ∈ E
*
P
(v 0 ,v)∈E
0
f (v , v) −
P
(v,v 00 )∈E
Kapazitätsrestriktion
f (v, v 00) = 0
∀v ∈ V − {q, s}
Flusserhaltung
169
Beispiel: Fluss
4/ 4
3/ 2
v0
v1
1/ 0 3/ 2
q
4/ 3
v2
Definition:
3/ 3
s
1/ 1
3/ 2
1/ 1
v3
5/ 4
• ein zunehmender Weg w von q nach s mit Kapazität k ist eine
0
• Kantenfolge ( v00 , v10 ), (v10 , v20 ), . . . , (vr−1
, vr0 ) mit
|{z}
|{z}
=q
=s
0
0
? f (vi0 , vi+1
) + k ≤ β(vi0 , vi+1
),
0
? f (vi+1
, vi0 ) − k ≥ 0,
(für i = 0, . . . r − 1)
0
falls (vi0 , vi+1
)∈E
0
falls (vi+1
, vi0 ) ∈ E
oder
170
Beispiel: Flusserhöhung
4/ 4
3/ 2
v0
1/ 0 3/ 2
q
4/ 3
v2
v1
s
1/ 1
3/ 2
1/ 1
v3
4/ 4
3/ 3
5/ 4
1/ 0 3/ 3
q
4/ 4
• Fluss f 0 nach Addition des zunehmenden Weges w:
0
0
f 0(vi0 , vi+1
) := f (vi0 , vi+1
) + k,
3/ 1
v0
v2
v1
3/ 3
s
1/ 1
3/ 3
1/ 1
v3
5/ 5
(für i = 0, . . . r − 1)
0
falls (vi0 , vi+1
)∈E
0
0
und f (vi0 , vi+1
) + k ≤ β(vi0 , vi+1
)
0
0
f 0(vi+1
, vi0 ) := f (vi+1
, vi0 ) − k,
sonst
171
Algorithmus von Ford/Fulkerson
für alle e ∈ E: f (e) = 0;
while ∃ zunehmenden Weg w von q nach s do
sei k die (max.) Kapazität von w
erhöhe f entlang w um k
• zur Implementierung: speichere f (e) für jede Kante e
• bei naiver Wahl von w ist weder die Termination noch die Korrektheit garantiert
Aufwand im schlechtesten Fall:
• abhängig vom Vorgehen bei der Bestimmung des zunehmenden Weges
1) wenn w mit maximaler Kapazität: O(m · log βmax)
2) wenn w mit minimaler Kantenanzahl: O(n · m2)
172
Andere Algorithmen zur Bestimmung des maximalen Flusses
• Algorithmus von Dinic: O(n2 · m)
• Algorithmus von Karsanow/Tarjan: O(n3)
• Algorithmus von Sleator/Tarjan: O(n · m · log n)
• Details siehe Ottmann/Widmayer
Bemerkungen:
• maximaler Fluss entspricht “minimalem Schnitt”
• ein Fluss f ist maximal, wenn es keinen zunehmenden Weg gibt
Herunterladen