¢¡¤£¦¥¤§© ¤

Werbung
http://www.mpi-sb.mpg.de/~hannah/info5-ws00
IS
UN
R
S
WS 2000/2001
E R SIT
S
Schömer, Bast
SA
IV
A
Grundlagen zu
Datenstrukturen und Algorithmen
A VIE N
Lösungsvorschläge für das 12. Übungsblatt
Letzte Änderung am 31. Januar 2001
Aufgabe 1
Vorbemerkung. Wir interessieren uns für ungerichtete azyklische Graphen G = (V, E) mit
maximaler Kantenzahl. Eine einfache Induktion (über |V |) zeigt, dass jeder Graph mit |E| ≥ |V |
bereits zyklisch ist, oder äquivalent, G maximal azyklisch impliziert |E| = |V | − 1:
Der Induktionsbeginn (|V | = 3) ist klar. Sei nun also G = (V, E) ein maximaler azyklischer
Graph. Für den Induktionsschritt nutzt man aus, dass es ein v ∈ V mit deg(v) = 1 geben muss
(“≤”, sonst wäre G nicht azyklisch, “≥” wegen Maximalität). Entfernen von v und inzidenter
Kante e liefert G0 = (V − {v}, E − {e}), nach Induktionsvoraussetzung |E − {e}| = |V − {v}| − 1,
also ist |E| = |V | − 1.
Aus obigem folgt, dass |E| ≥ |V | schon impliziert, dass G zyklisch ist. Einer der Standardalgorithmen zum Finden von Zykeln (bfs, dfs, topSort. . . ) kann nun so modifiziert werden, dass erst
auf |E| ≥ |V | geprüft wird, und dann der eigentliche Algorithmus nur bei |E| < |V | durchgeführt
wird. In diesem Fall gilt für die Laufzeitschranke O(|E| + |V |) = O(|V |).
Aufgabe 2
Der Graph sei als Adjazenzliste mit eingehenden Knoten gegeben. (Diese kann man leicht in
O(|V | + |E|) aus einer Adjazenzliste mit ausgehenden Knoten erstellen.)
Um aus den verschiedenen Quellen eine einzige zu machen, fügt man einen neuen Startknoten s
in den Graphen ein und fügt eine Kante vom Gewicht 0 von s zu jeder der ursprünglichen Quellen
hinzu. Analog wird eine Senke t sowie Kanten mit Gewicht 0 von den ursprünglichen Senken zu
t hinzugefügt. Führe zunächst eine topologische Sortierung der Knoten durch.
Sei min[v] (max[v]) die aktuelle Länge des kürzesten (längsten) Pfades von s nach v und pmin[v]
(pmax[v]) der Vorgänger von v auf diesem Pfad. Setze zu Beginn min[v] = ∞ und max[v] = −1
für alle v ∈ V \ {s}, min[s] = max[s] = 0 sowie pmin[v] = pmax[v] =nil für alle v ∈ V .
Tue dann für jeden Knoten v in topologisch aufsteigender Ordnung folgendes:
for all e = (u, v) ∈ V :
if (min[u] + d(u, v) < min[v]) min[v] = min[u] + d(u, v); pmin[v] = u
if (max[u] + d(u, v) > max[v]) max[v] = max[u] + d(u, v); pmax[v] = u
Dann entspricht nach Ende des Algorithmus min[v] (max[v]) der Länge des kürzesten (längsten)
Pfades von s nach v, min[v] = ∞ (bzw. max[v] = −1) genau dann, wenn v nicht von s erreichbar
ist. Also entspricht min[t] (max[t]) der Länge des kürzesten (längsten) Pfades von S nach T , den
man mithilfe der pmin- bzw. pmax-Informationen zurückverfolgen kann.
Laufzeit: Die topologische Sortierung geht in O(|V |+|E|), die Initialisierung in O(|V |), die Schleife
in O(|V |+|E|) (für jede eingehende Kante eines Knotens hat man konstante Laufzeit, jeder Knoten
wird bearbeitet). Also insgesamt O(|V | + |E|).
Korrektheit:
Zeige: Nach Ausführung des Algorithmus ist für alle v ∈ V min[v] (max[v]) gleich der Länge des
kürzesten (längsten) Pfades von s nach v. Zeige dazu die Invariante: Nach Abarbeiten des i-ten
Knotens, ist schon min[v] (max[v]) die Länge des kürzesten (längsten) Pfades von s nach v für
alle v an den Positionen 1, ..., i.
Induktion über die Position i in der topologischen Sortierung:
i = 1: Der erste Knoten v in der Sortierung kann keine eingehenden Kanten haben, also wird
weder min[v] noch max[v] verändert. Dieser Knoten ist entweder der künstlich erzeugte Knoten s
oder ein isolierter Knoten. Ist er s, so bleiben min[v] und max[v] korrekterweise bei 0, ist er nicht
s, bleibt min[v] bei ∞ und max[v] bei −1, was ebenso korrekt ist, denn es existiert ja kein Pfad
von s zu diesem isolierten Knoten.
i → i + 1: Gelte die Induktionsannahme für den i-ten Knoten in der topologischen Sortierung.
Nun wird der (i + 1)−te Knoten v abgearbeitet. Dazu werden alle in diesen Knoten eingehenden
Kanten betrachtet. Der Algorithmus setzt dann min[v] = min(u,v)∈E {min[u] + d(u, v)} (bzw.
max[v] = max(u,v)∈E {max[u] + d(u, v)}. Nach Induktionsannahme ist jedes min[u] (max[u]) die
Länge des kürzesten (längsten) Weges von s nach u, also entspricht min[v] (max[v]) nun der
Länge des kürzesten (längsten) Weges von s nach v.
Aufgabe 3
a)
((A · B) · C)ij =
=
=
=
=
=
n
_
k=1
n
_
k=1
n
_
k=1
n
_
l=1
n
_
l=1
n
_
(A · B)ik ∧ Ckj
n
_
l=1
n
_
Ail ∧ Blk
!
∧ Ckj
Ail ∧ Blk ∧ Ckj
!
Ail ∧ Blk ∧ Ckj
!
l=1
n
_
k=1
Ail ∧
n
_
Blk ∧ Ckj
k=1
!
Ail ∧ (B · D)lj
l=1
= (A · (B · C))ij .
Dabei braucht man die Distributivität und die Assoziativität von ∨ und ∧.
b) Behauptung: Für k ∈ N und 1 ≤ i, j ≤ n ist (Ak )ij = 1 genau dann, wenn Knoten j von
Knoten i aus in ≤ k Schritten erreichbar ist.
Beweis mit Induktion über k.
k = 1: Das ist gerade die Definition der Adjazenzmatrix.
k → k + 1: Es ist
(Ak+1 )ij = (Ak · A)ij =
n
_
(Ak )il ∧ Alj = 1
l=1
genau dann, wenn ein 1 ≤ l ≤ n existiert mit
(Ak )il = 1
und
Alj = 1,
also (nach Induktionsvoraussetzung) genau dann, wenn ein 1 ≤ l ≤ n existiert, so daß
Knoten l von Knoten i aus in ≤ k Schritten erreichbar ist und Knoten j von Knoten l aus
in ≤ 1 Schritten erreichbar ist. Das ist also genau dann der Fall, wenn Knoten j von Knoten
i aus (auf dem Weg über Knoten l) durch ≤ k + 1 Schritte erreichbar ist.
c) Ist Knoten j von Knoten i aus durch irgendeinen Pfad erreichbar, so kann man einen solchen
Pfad mit ≤ n Schritten wählen. Hat dieser Pfad nämlich > n Schritte, so muß ein Zykel in
dem Pfad sein, da n die Anzahl der Knoten im Graphen ist. Man kann dann diesen Zykel
weglassen und erhält einen Pfad mit weniger Schritten. Das macht man solange, bis man
einen Pfad mit ≤ n Schritten gefunden hat.
Es ist also jeder Knoten j von einem Knoten i aus entweder nicht erreichbar, oder durch
einen Pfad mit ≤ n Schritten erreichbar. Das bedeutet, daß An = An+k ist für alle k ≥ 0.
Also ist
lim Ak = An .
k→∞
d) Nach Teil c) ist A∞ = An+k für alle k ≥ 0. Durch wiederholtes Quadrieren kann man A2
berechnen mit 2l−1 < n ≤ 2l , also
l = dlog2 ne.
l
Man muß dabei l mal quadrieren. Für jede Quadrierung muß man n2 Elemente bestimmen.
Zur Bestimmung von (A · B)ij braucht man n Vergleiche. Also ist die Laufzeit
O(n3 log n).
Man kann das wiederholte Quadrieren abbrechen, falls ein m existiert mit (Am )2 = Am , da
dann schon der Grenzwert erreicht wurde. Für diesen Algorithmus muß man sich aber bei
jeder Multiplikation ansehen, ob sich ein Element verändert oder nicht.
Zweite Möglichkeit: Nach Teil b) ist (A∞ )ij = 1 genau dann, wenn Knoten j von Knoten
i aus (auf irgendeinem Pfad) erreichbar ist. Man geht jetzt zeilenweise vor und bestimmt
A∞ direkt. In der i-ten Zeile setzt man zuerst alle (A∞ )ij = 0. Dann macht man vom iten Knoten aus BFS. Wird dabei ein Knoten j erreicht (einschließlich j = i), so setzt man
(A∞ )ij = 1.
Die Laufzeit für eine Zeile ist somit O(|V | + |E|). Damit hat man insgesamt eine Laufzeit
von O(|V |(|V | + |E|)). Mit |V | = n und |E| = O(n2 ) erhält man die Gesamtlaufzeit O(n3 ).
Aufgabe 4
a)
((A · B) · C)ij = min{(A · B)ik + Ckj |k = 1, ..., n}
= min{(min{Ait + Btk |t = 1, ..., n}) + Ckj |k = 1, ..., n}
=
=
=
=
min{Ait + Btk + Ckj |t, k = 1, ..., n}
min{Ait + (min{Btk + Ckj |k = 1, ..., n})|t = 1, ..., n}
min{Ait + (B · C)tj |t = 1, ..., n}
(A · B · C))ij
b) formal: Beweis durch Induktion über k:
k = 1: Enthält nach Definition der Distanzmatrix die kürzesten Wege der Länge ≤ 1.
k = x + y: Induktionsvoraussetzung: Ax und Ay enthalten jeweils die richtigen Werte für
kürzeste Wege der Länge ≤ x bzw. ≤ y
Induktionsschritt: (Ax · Ay )ij := min{Axik + Aykj |k = 1, ..., n}
Die Matrizenmultiplikationsvorschrift betrachtet die Wege von i nach j über k.
Da es keine anderen Wege geben kann und nach IV Ax und Ay korrekt sind liefert die
Minimumbildung der summierten Wegkosten den kürzesten Weg der maximalen Länge x +
y = k.
c) Da kürzeste Wege in G maximal alle Knoten einmal besuchen können (sonst könnte man
einen Zyklus unter Wegverkürzung herausschneiden) ergibt sich eine obere Weglänge von
n = Anzahl der Knoten.
D.h. spätestens bei An kann sich keine Änderung mehr ergeben, weil längere Pfade nicht
sinnvoll wären!
d) find_shortestpaths(matrix A)
{
matrix B = A * A;
if (A==B)
return (B)
else
return (find_shortestpaths(B));
}
Der Algorithmus verdoppelt immer die maximal zulässige Weglänge.
Sollte sich bei der Matrizenmultiplikation nichts verändert haben, dann ist man fertig.
Wenn sich etwas verändert hat, dann muss man die Routine nochmal mit der neuen Matrix
aufrufen.
Laufzeitanalyse:
• Die Matrizenmultiplikation muss für jeden der Einträge (= n · n) die Länge der Wege über alle möglichen Knoten (= n) ausrechnen und dann das Minimum eintragen
(O(n)).
Daraus ergibt sich für diesen Punkt eine Gesamtlaufzeit von O(n3 ).
• Aus Teil c) wissen wir das man höchstens An berechnen muss.
Da sich die Weglänge immer verdoppelt müssen also log n Multiplikationen ausgeführt
werden.
• Damit ergibt sich die geforderte Laufzeit von O(n3 log n)!
Herunterladen