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
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.
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:
- Ein Pfeilfeld listet in Zeile die Nummer eines Pfeils aus , sortiert nach Zielknoten.
- Ein Knotenfeld speichert je Knoten den Index seines ersten Eintrags in .
Die Vorgänger von stehen dann zwischen und in .
Rückwärtsstern am Beispiel
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 2 | 5 | 7 | 3 | 8 | 6 |
Pfeilfeld
| 1 | 2 | 3 | 4 | 5 | (6) | |
|---|---|---|---|---|---|---|
| 1 | 1 | 3 | 6 | 8 | 9 |
Knotenfeld
Die Vorgänger von Knoten 3: und die Pfeile , also , und .
Kürzeste Wege
Aufgabenstellung
Gesucht: die kürzesten Wege von einem Startknoten (Quelle) 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.
Die Schlange im Feld
Die markierten Knoten stehen in einem Feld , ein Eintrag je Knoten:
- : der Knoten, der in der Schlange auf folgt.
- hinten einreihen: und .
- Nächster Kopf: .
Die FIFO-Liste L hält den ganzen Lauf fest: Abgearbeitete Knoten werden gestrichen, die übrigen bilden die aktuelle Schlange.
Beispiel: Busliniennetz
Kürzeste Wege von Knoten zu allen anderen.
Frage: Welchen Weg vermuten Sie von 1 nach 3: direkt oder über Umwege?
Auf der nächsten Folie suchen Sie die kürzesten Wege von Knoten 1 zu allen anderen Knoten. Für jeden Knoten wählen Sie den Pfeil, über den er erreicht wird. Stimmt Ihre Vermutung zu Knoten 3?
Sie sind dran
Der Ablauf im Detail
Jetzt rechnet FIFO dasselbe, Knoten für Knoten.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | |
| – | – | – | – | – | – | – |
L = (1)
Start: .
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | ∞ | ∞ | ∞ | ∞ | |
| – | 1 | 1 | – | – | – | – |
L = (1, 2, 3)
Knoten 1: beide Nachfolger erhalten Marken und stellen sich hinten an.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | 23 | ∞ | 73 | ∞ | |
| – | 1 | 1 | 2 | – | 2 | – |
L = (1, 2, 3, 4, 6)
Knoten 2: 4 und 6 sind neu. Für 3 gilt : keine Korrektur.
| 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)
Knoten 3: Knoten 5 erhält .
| 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)
Knoten 4 korrigiert: . Knoten 3 stellt sich erneut an! Auch (6 wartet bereits).
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 53 | 70 | 88 | |
| – | 1 | 4 | 2 | 3 | 4 | 6 |
L = (1, 2, 3, 4, 6, 5, 3, 7)
Knoten 6: Knoten 7 erhält und stellt sich hinten an.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 53 | 64 | 78 | |
| – | 1 | 4 | 2 | 3 | 5 | 5 |
L = (1, 2, 3, 4, 6, 5, 3, 7, 6)
Knoten 5 korrigiert 6 () und 7 (). 6 stellt sich erneut an, 7 wartet.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 64 | 78 | |
| – | 1 | 4 | 2 | 3 | 5 | 5 |
L = (1, 2, 3, 4, 6, 5, 3, 7, 6, 5)
Knoten 3 korrigiert 5 (). 5 stellt sich erneut an.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 64 | 78 | |
| – | 1 | 4 | 2 | 3 | 5 | 5 |
L = (1, 2, 3, 4, 6, 5, 3, 7, 6, 5)
Knoten 7 hat keine Nachfolger, es ändert sich nichts.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 64 | 78 | |
| – | 1 | 4 | 2 | 3 | 5 | 5 |
L = (1, 2, 3, 4, 6, 5, 3, 7, 6, 5)
Knoten 6 prüft 7: , also keine Korrektur.
| 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)
Knoten 5 korrigiert 6 () und 7 (). Beide stellen sich erneut an.
| 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)
Knoten 6 prüft 7: , also keine Korrektur.
| 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)
Knoten 7 hat keine Nachfolger. Wegen ist die Schlange leer: Abbruch.
Ergebnis als Baum
Die roten Pfeile bilden den Kürzeste-Wege-Baum.
FIFO-LIFO
Der FIFO-LIFO-Algorithmus
Ein Knoten in der Liste löst Korrekturen bei seinen Nachfolgern aus, bei deren Nachfolgern usw. Muss er erneut hinein, lohnt es nicht, erst die Knoten abzuarbeiten, die er vorher hineingebracht hat: Sie werden voraussichtlich wieder korrigiert.
Wer zum wiederholten Mal in die Liste kommt, wird deshalb vorne hinter dem Kopf eingereiht: das FIFO-LIFO-Prinzip.
Vorne oder hinten?
Frage: Woran erkennen Sie, dass ein Knoten schon einmal in der Liste stand?
Bis auf starten alle Knoten mit . Ist vor der Korrektur schon endlich, stand schon in der Liste. Steht er nicht mehr in der Schlange, ist er bereits bearbeitet: Er kommt vorne hin.
Ein zusätzliches Feld ginge auch, ist so aber nicht nötig.
Vorne einreihen im Feld
Ausschnitt aus einem Lauf mit sieben Knoten:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| – | – | 6 | – | 3 | 4 | – |
L = (5, 3, 6, 4)
Kopf , Ende . Knoten 1 ist zum zweiten Mal aufzunehmen.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 3 | – | 6 | – | 1 | 4 | – |
L = (5, 1, 3, 6, 4)
, dann (allgemein und ).
Ablauf von FIFO-LIFO
Start wie bei FIFO: .
- Für jeden Nachfolger : ist ?
- Falls ja: , ; steht nicht in der Schlange, wird er eingereiht:
- hinten, falls bisher : , ;
- sonst vorne hinter : , (ist , auch ).
- Ist : Abbruch; sonst .
FIFO-LIFO am Busliniennetz
Dasselbe Netz, aber wer erneut markiert wird, kommt vorne hin.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | |
| – | – | – | – | – | – | – |
L = (1)
Start: .
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | ∞ | ∞ | ∞ | ∞ | |
| – | 1 | 1 | – | – | – | – |
L = (1, 2, 3)
Knoten 1: beide Nachfolger erhalten erstmals Marken und stellen sich hinten an.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 35 | 23 | ∞ | 73 | ∞ | |
| – | 1 | 1 | 2 | – | 2 | – |
L = (1, 2, 3, 4, 6)
Knoten 2: 4 und 6 sind neu und kommen hinten hin. Für 3 gilt : keine Korrektur.
| 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)
Knoten 3: Knoten 5 erhält und stellt sich hinten an.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 53 | 70 | ∞ | |
| – | 1 | 4 | 2 | 3 | 4 | – |
L = (1, 2, 3, 4, 3, 6, 5)
Knoten 4 korrigiert 3 (). war endlich: 3 kommt vorne hin. Auch (6 wartet).
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 70 | ∞ | |
| – | 1 | 4 | 2 | 3 | 4 | – |
L = (1, 2, 3, 4, 3, 6, 5)
Knoten 3 ist sofort wieder dran und korrigiert 5 (), bevor 5 überhaupt bearbeitet wird.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 0 | 11 | 33 | 23 | 51 | 70 | 88 | |
| – | 1 | 4 | 2 | 3 | 4 | 6 |
L = (1, 2, 3, 4, 3, 6, 5, 7)
Knoten 6: Knoten 7 erhält erstmals und stellt sich hinten an.
| 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, 3, 6, 5, 6, 7)
Knoten 5 korrigiert 6 () und 7 (). 6 kommt vorne hin, 7 wartet.
| 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, 3, 6, 5, 6, 7)
Knoten 6 prüft 7: , also keine Korrektur.
| 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, 3, 6, 5, 6, 7)
Knoten 7 hat keine Nachfolger. Wegen ist die Schlange leer: Abbruch.
Varianten des Verfahrens
- FIFO-LIFO braucht am Busliniennetz 9 statt 12 Iterationen bei gleichen Distanzen: 5 und 7 kommen nur einmal dran. Das spart nur, wenn Knoten erneut in die Schlange kommen; sonst sind beide gleich schnell.
- FIFO-FIFO: mehrere erneut aufzunehmende Knoten behalten ihre Reihenfolge, eingefügt hinter dem zuletzt eingefügten Knoten.
Beide Varianten reduzieren überflüssige Aktualisierungen.
Netzwerkfluss
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 .
Ist nur der kürzeste Weg zu einem Zielknoten (Senke) gesucht, fließt genau eine Einheit von nach .
Das Modell
Flusserhaltung (Zufluss Abfluss Bedarf) für jeden Knoten :
Das Modell am Beispiel
Flusserhaltung, links die Knoten 1 bis 4, rechts 5 bis 7:
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[3]: 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).
- FIFO-LIFO reiht erneut aufzunehmende Knoten vorne ein und spart so Iterationen, wenn Marken korrigiert werden.
- Kürzeste Wege sind ein Spezialfall des Netzwerkflussproblems, lösbar als LP.