Vorlesung 06: Graphentheorie
Grundlagen Operations Research
Grundbegriffe
Netze überall
Knoten, Kanten, Pfeile
- Knotenmenge : die Objekte (Orte, Stationen, …).
- Kante : ungerichtete Verbindung.
- Pfeil : gerichtete Verbindung.
- Bewertung : Kosten, Distanz oder Zeit einer Kante bzw. eines Pfeils.
Ein schlichter Graph hat keine Schlingen und keine parallelen Kanten; ein Digraph ist ein endlicher, schlichter, gerichteter Graph.
Gerichtet und ungerichtet
Ungerichteter Graph
Digraph
Spezielle Graphen
- Zusammenhängend: unter Vernachlässigung der Richtung sind alle Knoten direkt oder indirekt verbunden.
- Baum: zusammenhängend und ohne Kreis.
- 1-Baum: zusammenhängend mit genau einem Kreis.
Der Kürzeste-Wege-Baum, den wir gleich berechnen, ist ein solcher Baum.
Kompakte Speicherung
Warum kompakt speichern?
Eine Knoten-Knoten-Matrix braucht Einträge, bei dünn besetzten Netzen enorme Verschwendung.
Besser: nur die tatsächlich vorhandenen Pfeile speichern (Vorwärtsstern).
Vorwärtsstern
Zwei Felder genügen:
- Ein Pfeilfeld listet alle Pfeile mit Bewertung , sortiert nach Startknoten.
- Ein Knotenfeld speichert je Knoten den Index seines ersten Pfeils in .
Die Nachfolger von stehen dann zusammenhängend zwischen und . Der Speicherbedarf sinkt von auf .
Vorwärtsstern am Beispiel
| 1 | 2 | 3 | 4 | 5 | (6) | |
|---|---|---|---|---|---|---|
| 1 | 3 | 4 | 5 | 7 | 9 |
Knotenfeld
Das Pfeilfeld listet darunter alle acht Pfeile, nach Startknoten sortiert.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|
| (1,2) | (1,3) | (2,4) | (3,2) | (4,3) | (4,5) | (5,3) | (5,4) | |
| 25 | 35 | 15 | 45 | 15 | 45 | 25 | 35 |
Nachfolger nachschlagen
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|
| (1,2) | (1,3) | (2,4) | (3,2) | (4,3) | (4,5) | (5,3) | (5,4) | |
| 25 | 35 | 15 | 45 | 15 | 45 | 25 | 35 |
Frage: Wie finden Sie die Nachfolger von Knoten 4?
Knotenfeld : und die Pfeile und , ohne die Matrix zu durchsuchen.
Der Rückwärtsstern
Für Vorgänger-Fragen (z. B. in der Netzplantechnik) sortiert man dieselben Pfeile nach dem Zielknoten:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 2 | 5 | 7 | 3 | 8 | 6 |
Feld : Pfeilindizes, nach Zielknoten sortiert. Dazu ein Einstiegsfeld je Zielknoten ().
Die Vorgänger von Knoten 3: Positionen bis Pfeile , also , und .
Kürzeste Wege
Aufgabenstellung
Gesucht: die kürzesten Wege von einem Startknoten zu allen anderen Knoten.
- : Bewertung des Pfeils (Zeit, Distanz, Kosten).
- : Menge der Nachfolger von .
- : bisher kürzeste Entfernung von nach ; Start: , sonst .
- : bester Vorgänger von (zum Rekonstruieren des Weges).
Der FIFO-Algorithmus
Knoten werden in der Reihenfolge einer FIFO-Warteschlange abgearbeitet. Findet man einen kürzeren Weg zu , wird aktualisiert und hinten angestellt: solange, bis die Schlange leer ist.
Ein Knoten steht höchstens einmal gleichzeitig in der Schlange, kann aber insgesamt mehrfach bearbeitet werden (Label-Correcting).
Iterative Vorgehensweise
Schlange mit Kopf und Ende ; Start .
- Für jeden Nachfolger : ist ?
- Falls ja: , , und hinten einreihen (falls nicht bereits enthalten).
- Ist : Schlange leer Abbruch.
- Sonst auf den nächsten Knoten der Liste setzen.
Beispiel: Busliniennetz
Kürzeste Wege von Knoten zu allen anderen.
Frage: Welchen Weg vermuten Sie von 1 nach 3: direkt oder über Umwege?
Der Ablauf im Detail
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | |
| – | – | – | – | – | – | – |
L = (1)
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | ∞ | ∞ | ∞ | ∞ | |
| – | 1 | 1 | – | – | – | – |
L = (1, 2, 3)
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | 23 | ∞ | 73 | ∞ | |
| – | 1 | 1 | 2 | – | 2 | – |
L = (1, 2, 3, 4, 6)
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | 23 | 53 | 73 | ∞ | |
| – | 1 | 1 | 2 | 3 | 2 | – |
L = (1, 2, 3, 4, 6, 5)
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 53 | 70 | ∞ | |
| – | 1 | 4 | 2 | 3 | 4 | – |
L = (1, 2, 3, 4, 6, 5, 3)
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 62 | 76 | |
| – | 1 | 4 | 2 | 3 | 5 | 5 |
L = (1, 2, 3, 4, 6, 5, 3, 7, 6, 5, 6, 7)
Start: .
Knoten 1: beide Nachfolger erhalten Marken und stellen sich hinten an.
Knoten 2: 4 und 6 sind neu. Für 3 gilt : keine Korrektur.
Knoten 3: Knoten 5 erhält .
Knoten 4 korrigiert: . Knoten 3 stellt sich erneut an! Auch (6 wartet bereits).
So läuft es weiter (5 korrigiert 6 und 7, das erneute 3 korrigiert 5, …), bis die Schlange leer ist.
Ergebnis als Baum
Die roten Pfeile bilden den Kürzeste-Wege-Baum.
Varianten des Verfahrens
- FIFO-LIFO: ein erneut aufzunehmender Knoten wird vorne hinter den Schlangenkopf gesetzt, seine veralteten Nachfolger werden so zuerst korrigiert.
- FIFO-FIFO: mehrere erneut aufzunehmende Knoten behalten ihre Reihenfolge, eingefügt hinter dem zuletzt eingefügten Knoten.
Beide Varianten reduzieren überflüssige Aktualisierungen.
Netzwerkflussproblem
Kürzeste Wege als LP
Kürzeste Wege lassen sich als Netzwerkflussproblem formulieren: Wir schicken von eine Einheit zu jedem anderen Knoten.
- : Kosten pro Mengeneinheit auf Pfeil .
- : Transportmenge (Fluss) auf Pfeil .
Das Modell
Flusserhaltung (Zufluss Abfluss Bedarf) für jeden Knoten :
Kontrolle mit Julia
using JuMP, HiGHS
kanten = [(1,2,11),(1,3,35),(2,3,25),(2,4,12),(4,3,10),(3,5,18),
(2,6,62),(4,6,47),(5,6,11),(5,7,25),(6,7,18)]
V, q = 1:7, 1
modell = Model(HiGHS.Optimizer); set_silent(modell)
@variable(modell, X[e in kanten] >= 0) # Fluss je Pfeil
@objective(modell, Min, sum(e[3] * X[e] for e in kanten)) # e = (i, j, Kosten)
for i in V # Flusserhaltung
zu = sum(X[e] for e in kanten if e[2] == i; init = 0.0)
ab = sum(X[e] for e in kanten if e[1] == i; init = 0.0)
@constraint(modell, zu - ab == (i == q ? 1 - length(V) : 1))
end
optimize!(modell)
println("Gesamtkosten des Flusses: F* = ", objective_value(modell))Gesamtkosten des Flusses: F* = 256.0
Der optimale Fluss
Der Fluss nutzt genau den Kürzeste-Wege-Baum:
, , , , , .
Die Flusskosten sind die Summe aller kürzesten Entfernungen.
Zusammenfassung
Das Wichtigste auf einen Blick
- Ein Graph besteht aus Knoten und (gerichteten) Kanten mit Bewertungen.
- Die kompakte Speicherung (Vorwärtsstern) spart bei dünnen Netzen enorm Speicher.
- Der FIFO-Algorithmus bestimmt kürzeste Wege von zu allen Knoten (Label-Correcting).
- Kürzeste Wege sind ein Spezialfall des Netzwerkflussproblems, lösbar als LP.