Grundlagen Operations Research
Vlćek u. a. (2024)
Ein schlichter Graph hat keine Schlingen und keine parallelen Kanten; ein Digraph ist ein endlicher, schlichter, gerichteter Graph.
Der Kürzeste-Wege-Baum, den wir gleich berechnen, ist ein solcher Baum.
Eine Knoten-Knoten-Matrix braucht Einträge, bei dünn besetzten Netzen enorme Verschwendung.
Besser: nur die tatsächlich vorhandenen Pfeile speichern (Vorwärtsstern).
Zwei Felder genügen:
Die Nachfolger von stehen dann zusammenhängend zwischen und . Der Speicherbedarf sinkt von auf .
| 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 |
| 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.
Für Vorgänger-Fragen (z. B. in der Netzplantechnik) sortiert man dieselben Pfeile nach dem Zielknoten:
Die Vorgänger von stehen dann zwischen und in .
| 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 .
Gesucht: die kürzesten Wege von einem Startknoten (Quelle) zu allen anderen Knoten.
Idee
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).
Schlange mit Kopf und Ende ; Start .
Die markierten Knoten stehen in einem Feld , ein Eintrag je Knoten:
Die FIFO-Liste L hält den ganzen Lauf fest: Abgearbeitete Knoten werden gestrichen, die übrigen bilden die aktuelle Schlange.
Kürzeste Wege von Knoten zu allen anderen.
Frage: Welchen Weg vermuten Sie von 1 nach 3: direkt oder über Umwege?
Gleich ausprobieren
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?
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.
Die roten Pfeile bilden den Kürzeste-Wege-Baum.
Kürzeste Distanzen
Idee
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.
Frage: Woran erkennen Sie, dass ein Knoten schon einmal in der Liste stand?
Erkennungsregel
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.
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 ).
Start wie bei FIFO: .
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.
Beide Varianten reduzieren überflüssige Aktualisierungen.
Kürzeste Wege lassen sich als Netzwerkflussproblem formulieren: Wir schicken von eine Einheit zu jedem anderen Knoten.
Ist nur der kürzeste Weg zu einem Zielknoten (Senke) gesucht, fließt genau eine Einheit von nach .
Flusserhaltung (Zufluss Abfluss Bedarf) für jeden Knoten :
Flusserhaltung, links die Knoten 1 bis 4, rechts 5 bis 7:
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 Fluss nutzt genau den Kürzeste-Wege-Baum:
, , , , , .
Schöner Zusammenhang
Die Flusskosten sind die Summe aller kürzesten Entfernungen.
Vorlesung 06: Graphentheorie | Prof. Dr. Knut Haase | Zur Webseite