Vorlesung 06: Graphentheorie

Grundlagen Operations Research

Grundbegriffe

Netze überall

Knoten, Kanten, Pfeile

  • Knotenmenge 𝒱\mathcal{V}: die Objekte (Orte, Stationen, …).
  • Kante [i,j][i, j] \in \mathcal{E}: ungerichtete Verbindung.
  • Pfeil (i,j)(i, j) \in \vec{\mathcal{E}}: gerichtete Verbindung.
  • Bewertung λij\lambda_{ij}: 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 |𝒱|2|\mathcal{V}|^2 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 BB listet alle Pfeile (ik,jk)(i_k, j_k) mit Bewertung λikjk\lambda_{i_k j_k}, sortiert nach Startknoten.
  • Ein Knotenfeld AA speichert je Knoten ii den Index kik_i seines ersten Pfeils in BB.

Die Nachfolger von ii stehen dann zusammenhängend zwischen kik_i und ki+11k_{i+1} - 1. Der Speicherbedarf sinkt von 𝒪(|𝒱|2)\mathcal{O}(|\mathcal{V}|^2) auf 𝒪(|𝒱|+||)\mathcal{O}(|\mathcal{V}| + |\vec{\mathcal{E}}|).

Vorwärtsstern am Beispiel

ii 1 2 3 4 5 (6)
kik_i 1 3 4 5 7 9

Knotenfeld AA

Das Pfeilfeld BB listet darunter alle acht Pfeile, nach Startknoten sortiert.

kk 1 2 3 4 5 6 7 8
(ik,jk)(i_k, j_k) (1,2) (1,3) (2,4) (3,2) (4,3) (4,5) (5,3) (5,4)
λikjk\lambda_{i_k j_k} 25 35 15 45 15 45 25 35

Nachfolger nachschlagen

kk 1 2 3 4 5 6 7 8
(ik,jk)(i_k, j_k) (1,2) (1,3) (2,4) (3,2) (4,3) (4,5) (5,3) (5,4)
λikjk\lambda_{i_k j_k} 25 35 15 45 15 45 25 35

Frage: Wie finden Sie die Nachfolger von Knoten 4?

Knotenfeld AA: k4=5k_4 = 5 und k51=6k_5 - 1 = 6 \Rightarrow die Pfeile (4,3)(4,3) und (4,5)(4,5), 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:

nn 1 2 3 4 5 6 7 8
k̃n\tilde k_n 1 4 2 5 7 3 8 6

Feld CC: Pfeilindizes, nach Zielknoten sortiert. Dazu ein Einstiegsfeld DD je Zielknoten (1,1,3,6,8,91, 1, 3, 6, 8, 9).

Die Vorgänger von Knoten 3: Positionen 33 bis 55 \Rightarrow Pfeile 2,5,72, 5, 7, also (1,3)(1,3), (4,3)(4,3) und (5,3)(5,3).

Kürzeste Wege

Aufgabenstellung

Gesucht: die kürzesten Wege von einem Startknoten qq zu allen anderen Knoten.

  • λij\lambda_{ij}: Bewertung des Pfeils (i,j)(i, j) (Zeit, Distanz, Kosten).
  • 𝒩(i)\mathcal{N}(i): Menge der Nachfolger von ii.
  • D[j]D[j]: bisher kürzeste Entfernung von qq nach jj; Start: D[q]=0D[q] = 0, sonst \infty.
  • R[j]R[j]: bester Vorgänger von jj (zum Rekonstruieren des Weges).

Der FIFO-Algorithmus

TippIdee

Knoten werden in der Reihenfolge einer FIFO-Warteschlange abgearbeitet. Findet man einen kürzeren Weg zu jj, wird D[j]D[j] aktualisiert und jj 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 HH und Ende TT; Start H=T=qH = T = q.

  • Für jeden Nachfolger j𝒩(H)j \in \mathcal{N}(H): ist D[j]>D[H]+λH,jD[j] > D[H] + \lambda_{H,j}?
  • Falls ja: D[j]:=D[H]+λH,jD[j] := D[H] + \lambda_{H,j}, R[j]:=HR[j] := H, und jj hinten einreihen (falls nicht bereits enthalten).
  • Ist H=TH = T: Schlange leer \Rightarrow Abbruch.
  • Sonst HH auf den nächsten Knoten der Liste setzen.

Beispiel: Busliniennetz

Kürzeste Wege von Knoten q=1q = 1 zu allen anderen.

Frage: Welchen Weg vermuten Sie von 1 nach 3: direkt oder über Umwege?

Der Ablauf im Detail

jj 1 2 3 4 5 6 7
D[j]D[j] 0
R[j]R[j]

L = (1)

jj 1 2 3 4 5 6 7
D[j]D[j] 0 11 35
R[j]R[j] 1 1

L = (1, 2, 3)

jj 1 2 3 4 5 6 7
D[j]D[j] 0 11 35 23 73
R[j]R[j] 1 1 2 2

L = (1, 2, 3, 4, 6)

jj 1 2 3 4 5 6 7
D[j]D[j] 0 11 35 23 53 73
R[j]R[j] 1 1 2 3 2

L = (1, 2, 3, 4, 6, 5)

jj 1 2 3 4 5 6 7
D[j]D[j] 0 11 33 23 53 70
R[j]R[j] 1 4 2 3 4

L = (1, 2, 3, 4, 6, 5, 3)

jj 1 2 3 4 5 6 7
D[j]D[j] 0 11 33 23 51 62 76
R[j]R[j] 1 4 2 3 5 5

L = (1, 2, 3, 4, 6, 5, 3, 7, 6, 5, 6, 7)

Start: H=T=q=1H = T = q = 1.

Knoten 1: beide Nachfolger erhalten Marken und stellen sich hinten an.

Knoten 2: 4 und 6 sind neu. Für 3 gilt 11+25=36>3511 + 25 = 36 > 35: keine Korrektur.

Knoten 3: Knoten 5 erhält 35+18=5335 + 18 = 53.

Knoten 4 korrigiert: 23+10=33<3523 + 10 = 33 < 35. Knoten 3 stellt sich erneut an! Auch D[6]=70D[6] = 70 (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.

WichtigKürzeste Distanzen

D=(0,11,33,23,51,62,76) D = (0,\, 11,\, 33,\, 23,\, 51,\, 62,\, 76)

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 qq eine Einheit zu jedem anderen Knoten.

  • cijc_{ij}: Kosten pro Mengeneinheit auf Pfeil (i,j)(i, j).
  • XijX_{ij}: Transportmenge (Fluss) auf Pfeil (i,j)(i, j).

Das Modell

Minimiere F=(i,j)cijXij \text{Minimiere } F = \sum_{(i,j) \in \vec{\mathcal{E}}} c_{ij}\, X_{ij}

Flusserhaltung (Zufluss - Abfluss == Bedarf) für jeden Knoten ii:

j:(j,i)Xjij:(i,j)Xij={1|𝒱|i=q1sonst \sum_{j:(j,i)} X_{ji} - \sum_{j:(i,j)} X_{ij} = \begin{cases} 1 - |\mathcal{V}| & i = q \\ 1 & \text{sonst} \end{cases}

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:

X12=6X_{12} = 6, X24=5X_{24} = 5, X43=4X_{43} = 4, X35=3X_{35} = 3, X56=1X_{56} = 1, X57=1X_{57} = 1.

WichtigSchöner Zusammenhang

F*=256=jD[j] F^* = 256 = \sum_j D[j] 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 qq zu allen Knoten (Label-Correcting).
  • Kürzeste Wege sind ein Spezialfall des Netzwerkflussproblems, lösbar als LP.

Literatur

Literaturverzeichnis

Domschke, Wolfgang, Andreas Drexl, Robert Klein, und Armin Scholl. 2015. Einführung in Operations Research. 9. Aufl. Springer Gabler. https://doi.org/10.1007/978-3-662-48216-2.
Vlćek, Tobias, Knut Haase, Malte Fliedner, und Tobias Cors. 2024. „Police service district planning“. OR Spectrum, 1–33.