Gliederung 5. Compiler 1. 2. 3. 4. Struktur eines Compilers Syntaxanalyse durch rekursiven Abstieg Ausnahmebehandlung Arrays und Strings 6. Sortieren und Suchen 1. Grundlegende Datenstrukturen 2. Bäume 3. Hash-Verfahren (Streuspeicherung) 7. Graphen 1. Darstellung von Graphen 2. Algorithmen auf Graphen 1. Kürzeste Wege 2. Fluß- und Zuordnungsprobleme 3. Topologisches Sortieren Kürzeste Wege mit negativen Kantengewichten • Graphen können negative Kantengewichte enthalten: Beispiel: Verbindungen, die mit negativen Kosten versehen sind (also Gewinnen) bevorzugt gewählt werden • Dijkstras Algorithmus funktioniert nur, wenn die Kantengewichte keine negative Werte annehmen können Beispiel: Eine lokal ungünstige nächste Kante kann durch eine darauf folgende negativ gewichtete Kante nachträglich zu einer besseren Verbindung führen • Lösung: Bellman-Ford-Algorithmus Vorgehensweise: – In mehreren Durchläufen wird jeweils die beste bisher mögliche Verbindung bestimmt – Im i-ten Durchlauf werden alle Pfade der Länge i berücksichtigt – Der längste Pfad ohne Zyklus hat eine Länge von |V|-1: Der Prozess kann nach |V|-1 Durchläufe abgeschlossen werden Graph mit negativen Kantengewichten u v 1 10 -2 s 3 -5 6 7 5 x 2 y • Merke: Falls es einen negativen Zyklus auf einem Pfad gibt, dann ist der kürzeste Pfad nicht wohldefiniert, da man durch Hinzufügen weiterer Zyklusrunden das Pfadgewicht beliebig klein machen kann. In diesem Fall existiert kein kürzester Pfad Bellman-Ford-Algorithmus (1) Vorüberlegungen • Das Gewicht einer Kante e wird in der Kantenvariablen e.weight gespeichert • Das Gewicht eines (aktuellen) kürzesten Pfades zum Knoten v wird in der Knotenvariablen v.weight gespeichert • Den Vorgänger u von v auf dem kürzesten Pfad von s nach v speichert man in der Knotenvariablen v.parent = u • Mit Hilfe der Parent-Variablen kann man den kürzesten Pfad zu einem Knoten v bestimmen Bellman-Ford-Algorithmus (2) • Initialisierung Initialize-BFA (G,s): 1. Für jeden Knoten v ∈ V: Setze v.weight = ∞ und v.parent = -1 2. s.weight = 0 • Operation „Relaxation“: – Solange man den kürzesten Pfad von s zu einem Knoten v noch nicht gefunden hat, speichert man in v.weight eine obere Schranke für die Länge des kürzesten pfades von s nach v – Eine Kante (u,v) zu „entspannen“, bedeutet, dass man testet, ob man einen neuen kürzesten Pfad von s nach v bekommt, wenn man den aktuellen kürzesten Pfad von s nach u betrachtet und die Kante (u,v) hinzufügt Relax(e=(u,v)): 1. Falls v.weight > u.weight + e.weight : 2. Setze v.weight = u.weight + e.weight und v.parent = u Bellman-Ford-Algorithmus (1) • Der Algorithmus gibt einen booleschen Wert zurück. Der Wert ist TRUE, falls es keinen negativen Zyklus gibt und der Algorithmus dann erfolgreich alle kürzesten Pfade zu den von s aus erreichbaren Knoten bestimmt hat • Gibt es einen negativen Zyklus, der von s aus erreichbar ist, so gibt der Algorithmus FALSE zurück Bellman-Ford(G,w,s): Initialize-Single-Source(G,s) Für i = 1, ... , n-1 : Für alle Kanten e = (u,v) ∈ E: Relax(e) Für alle Kanten e = (u,v): IF v.weight > u.weight + e.weight THEN Gib FALSE zurück Gib TRUE zurück. • Aufwand des Bellman-Ford-Algorithmus: Der Bellman-FordAlgorithmus hat Laufzeit O(nm), wobei n die Zahl der Knoten in V und m die Zahl der Kanten in E ist Bellman-Ford-Algorithmus: Beispiel (1) u 1 ∞ v u ∞ 10 1 ∞ 10 10 -2 s 0 v 3 -5 6 7 5 ∞ 2 -2 s 0 3 x y Graph nach der Initialisierung 6 7 5 ∞ -5 5 2 ∞ x 1. Iteration (i=1) Merke: • Nach der ersten Iteration hat der Knoten u einen vorläufigen Wert von 10 bekommen, da er direkt vom Startknoten s aus mit einer Kante des Gewichtes 10 erreicht werden kann • Der Wert 10 stellt nur einen Zwischenergebnis dar: Mit einem Pfad der Länge 2 über x kann eine billigere Verbindung mit Kosten 5+3=8 aufgebaut werden y Bellman-Ford-Algorithmus: Beispiel (1) u 2. Iteration (i=2) v 1 8 u s 0 3 -5 6 5 x 3 s 0 3 9 -5 6 7 5 5 x 2 4 y 2 3. Iteration (i=3) 10 -2 -5 6 7 v 1 8 s 0 5 x 7 y 2 u -2 5 7 5 9 10 10 -2 1 8 11 v 4. Iteration (i=4) 7 y Maximaler Durchfluss (1) • Motivation: Lösung logistischer Aufgaben • Beispiele: – Anzahl der Autos, die durch ein Kanalnetz fahren können – Strommenge, die durch ein Leitungsnetz fließen kann – Paketvermittlung in Computernetzwerken: Sollen vom Rechner Quelle möglichst (oder gar in Echtzeit) viele Pakete zum Rechner Ziel übertragen werden, stellt sich die Frage nach der maximalen Anzahl übertragbarer Pakete Quelle Ziel Maximaler Durchfluss (2) • Aufgabenbeschreibung 1. Menge von Computern (=Knoten eines Graphen), einer bildet die Quelle einer zu übermittelnden Datenmenge, einer die Senke 2. Zwei verschiedene Computer sind über Einzelverbindungen (=Kanten) verknüpft und können Daten über diese Verbindung übertragen • Daten werden als Pakete fester Länge übertragen • Jede Verbindung hat eine Kapazität (Maximale Menge pro Zeiteinheit zu übertragender Datenpakete) 3. Die Vernetzung ist derart, dass verschiedene Pfade bestehen, über die Daten von der Quelle zur Senke transportiert werden können. • Fragestellung: wie viele Datenpakete können pro Zeiteinheit maximal von der Quelle bis zur Senke übertragen werden? Maximaler Durchfluss (3) • Ein Netzwerk ist ein gerichteter Graph G = (V, E, c) mit ausgezeichneten Knoten q (Quelle) und s (Senke), sowie einer Kapazitätsfunktion c: E -> R+ • Ein Fluss für das Netzwerk ist eine Funktion f: E -> R, mit so dass gilt: - Kapazitätsbeschränkung ∀u,v∈V: f(u,v) ≤ c(u,v) - Konsistenz des Flusses ∀u,v∈V: f( (u,v) ) = -f( (v,u) ) - Flusserhaltung: ∀u,v∈V\{q,s}: Σu ∈ V f(u,v) =0 • Der Wert f(u,v) kann positiv oder negativ sein • Der Wert eines Flusses wird als w(f)=Σu ∈ V f(s,v) definiert • Der maximale Fluss ist folgendermaßen definiert: max{val(G, f, q) | f ist ein zulässiger Fluss in G bezüglich q und s} Der Ford-Fulkerson-Algorithmus • Ein „erweiternder (zunehmender) Weg“ in G ist eine Folge von Knoten beginnend bei s und endend bei q wobei der Fluss entlang dieses Weg vergrößert wird • Ford-Fulkerson: Generischer Algorithmus Pseudo-Code-Notation Finde einen zulässigen Fluss f in G REPEAT erhöhe diesen Fluss f UNTIL eine Erhöhung von f ist nicht möglich Der Ford-Fulkerson-Algorithmus Sei f ein zulässiger Fluss für G = (V,E). • Die Restkapazität einer Kante (u,v) wird folgendermaßen definiert: rest(u,v)= c(u,v)-f(u,v) • Der Restgraph von G (bzgl. f) wird mit Gf=(V, Ef) bezeichnet, wobei gilt: Ef ={(u,v) ∈ V X V: r(u,v)>0 } • Jeder gerichtete Pfad von q nach s im Restgraphen heißt zunehmender Weg • Ein Schnitt (A,B) eines Netzwerks ist eine Zerlegung von V in disjunkte Teilmengen A und B, so dass q ∈ A und s ∈ B. Die Kapazität des Schnitts ist c(A,B) = Σu∈A,v∈B c(u,v) Theorem (Max-Flow- Min-Cut) Sei f zulässiger Fluß für G. Folgende Aussagen sind äquivalent: 1. f ist maximaler Fluss in G. 2. Der Restgraph von f enthält keinen zunehmenden Weg. 3. w(f) = c(A,B) für einen Schnitt (A,B) von G Ford-Fulkerson: verbesserter Algorithmus for (alle (u,v) in E ) f(u,v) = 0; // Initialisierung while ( es gibt zunehmenden Weg p im Restgraphen Gf ) { r = min{rest(u,v) | (u,v) liegt in p}; for (alle (u,v) auf Pfad p ){ f(u,v) = f(u,v) + r(u,v) ; f(v,u) = f(v,u) - r; } } Ford-Fulkerson-Algorithmus: Beispiel (1) 1. Schritt: Graph wird mit Fluss=0 initialisiert Kantenbeschriftung: f/c/r f: Fluss c: Vorgegebene Kapazität r: Restkapazität 0/2/2 a 0/3/3 q b 0/1/1 0/4/4 c 0/2/2 0/1/1 0/2/2 0/2/2 e 0/2/2 0/2/2 0/3/3 d 0/1/1 0/3/3 s Ford-Fulkerson-Algorithmus: Beispiel (2) 1. Iteration: Auswahl des ersten Pfades: Pfad q->c->e->s mit r = min{rest(u,v) | (u,v) liegt in p}=2 wird ausgewählt 0/2/2 a 0/3/3 q b 0/1/1 0/4/4 c 0/2/2 0/1/1 0/2/2 0/2/2 e 0/2/2 0/2/2 0/3/3 d 0/1/1 0/3/3 s Ford-Fulkerson-Algorithmus: Beispiel (3) 2. Iteration: Anpassung der Kantenbeschriftungen und Auswahl des zweiten Pfades q->e->d->s mit r = min{rest(u,v) | (u,v) liegt in p}=1 Merke: Restkapazität von (c,e) ist nach der ersten Iteration erschöpft 0/2/2 a 0/3/3 q b 0/1/1 2/4/2 c 0/2/2 0/1/1 0/2/2 2/2/0 e 0/2/2 0/2/2 0/3/3 d 0/1/1 2/3/1 s Ford-Fulkerson-Algorithmus: Beispiel (4) 3. Iteration: Anpassung der Kantenbeschriftungen und Auswahl des dritten Pfades q->a->b->s mit r = min{rest(u,v) | (u,v) liegt in p}=2 Merke: • Restkapazität von (e,d) ist nach der zweiten Iteration aufgebraucht • Bei der Anpassung der Kantenbeschriftung (e,d) muss die Rückkante (d,e) mitbehandelt werden: f(v,u) = f(v,u) – r (Siehe Algorithmus) 0/2/2 a 0/3/3 q b 0/1/1 2/4/2 c 0/2/2 -1/1/2 1/2/1 2/2/0 e 0/2/2 0/2/2 1/3/2 d 1/1/0 2/3/1 s Ford-Fulkerson-Algorithmus: Beispiel (4) 4. Iteration: Anpassung der Kantenbeschriftungen und Auswahl des vierten Pfades q->a->d->e->s mit r = min{rest(u,v) | (u,v) liegt in p}=1 Merke: Restkapazität von (e,d) und (b,s) sind nach der dritten Iteration aufgebraucht 2/2/0 a 2/3/1 q b 0/1/1 2/4/2 c 0/2/2 -1/1/2 1/2/1 2/2/0 e 2/2/0 0/2/2 1/3/2 d 1/1/0 2/3/1 s Ford-Fulkerson-Algorithmus: Beispiel (4) 5. Iteration: Anpassung der Kantenbeschriftungen und Auswahl des fünften Pfades q->c->d->s mit r = min{rest(u,v) | (u,v) liegt in p}=2 Merke: • Restkapazität von (a,d) und (e,s) sind nach der vierten Iteration aufgebraucht • Bei der Anpassung der Beschriftung für die Kante (d,e) muss die Rückkante behandelt werden 2/2/0 a 3/3/0 q b 1/1/0 2/4/2 c 0/2/2 0/1/1 1/2/1 2/2/0 e 2/2/0 0/2/2 1/3/2 d 0/1/1 3/3/0 s Ford-Fulkerson-Algorithmus: Ergebnis • Es sind keine weitere Pfade möglich • Die Berechnung des maximalen Flusses ist damit beendet! • Merke: – Welcher Pfad in jedem Iterationsschritt gewählt wird bleibt offen! – Die Anzahl der Iterationen hängt von der Wahl der Pfade ab 2/2/0 •a 3/3/0 •q •b 4/4/0 •c 2/2/0 0/1/1 1/2/1 2/2/0 1/1/0 2/2/0 •e 0/2/2 •d 3/3/0 0/1/1 3/3/0 •s Weitere Algorithmen für Graphen Topologisches Sortierung • Gegeben: Gerichteter azyklischer Graph G • Gesucht: Reihenfolge der Knoten derart, dass jeder Knoten nach all seinen Vorgängern kommt (d.h. es sollte keine Rückkante geben) Falls die gerichtete Kanten kausale oder zeitliche Abhängigkeiten darstellen, so spricht man von scheduling- Problemen Eine topologische Sortierung eines DAG ist eine (lineare) Ordnung aller Knoten, so dass für alle Kanten (u,v) des Graphen gilt: Der Knoten u erscheint in der Ordnung vor v Topologische Sortierung: Beispiel Beliebtes Beispiel aus Lehrbüchern: Ein zerstreuter Professor hat Probleme, morgens seine Kleidungsstücke in der richtigen Reihenfolgen anzulegen. Daher legt er die Reihenfolgebedingungen beim Ankleiden fest: – – – – – – – – – Unterhose vor Hose Hose vor Gürtel Hemd vor Gürtel Gürtel vor Jackett Hemd vor Krawatte Krawatte vor Jackett Socken vor Schuhen Hose vor Schuhen Uhr: Egal Socken Unterhose Uhr Schuhe Hose Hemd Gürtel Krawatte Jackett Topologische Sortierung: Algorithmus 1. Starte die Methode Tiefendurchlauf und berechne die „finishing-time“ für alle Knoten (Zeit an der der Knoten mit dem Tiefendurchlauf besucht wurde) 2. Wenn ein Knoten abgearbeitet ist, dann füge ihn am Anfang der (Ordnungs-)Liste ein 3. Die Ordnungsliste stellt die topologische Sortierung dar Ergebnis mit finishing Time 18 Socken 16 Unterhose 15 Hose 14 Schuhe 10 Uhr 8 Hemd 7 Gürtel 5 Krawatte 4 Jackett Traveling Salesman Problem • Ein Handlungsreisender muss eine Reihe von Orten besuchen. • Er möchte seine Tour so planen, dass der zurückzulegende Weg (ein Zyklus, der alle Orte enthält) minimal ist. • Dies ist ein Problem über einen bewerteten Graphen. • Die Knoten sind die zu besuchenden Orte, die (bewerteten) Kanten stellen die Entfernung (oder Fahrtkosten) zwischen den Orten dar. • Das Graphenproblem lautet also : Finde einen Zyklus minimaler Länge, in einem vorgegebenen Graphen, der alle Knoten enthält. Traveling Salesman : Beispiel 1 Finden Sie eine TSP-Tour in dem unten stehenden Graphen rund um das Schloss von Marburg Köln 5 174 Marburg Bonn 224 106 3 Gießen 66 181 1 88 6 Mannheim 4 Kassel 96 7 30 34 0 104 2 Fulda 104 Frankfurt 136 93 8 Würzburg Eigenschaften des TSP • Unser Beispielproblem lässt sich offenbar intuitiv auf einfache Weise (und eindeutig!) lösen. Grundsätzlich ist das jedoch keineswegs der Fall. - (a) Oft gibt es überhaupt keine Lösung. Beispiel: Wenn ein weiterer Knoten "Mainz" zwischen Bonn und Frankfurt aufgenommen wird, ist das TSP nicht lösbar. - (b) Oft gibt es mehrere Zyklen, die alle zu besuchenden Knoten enthalten. Dann kann der kürzeste Zyklus nur durch Ausprobieren gefunden werden. Beispiel: Wenn zusätzlich eine Verbindung Mainz Mannheim aufgenommen wird, gibt es mehrere Zyklen. Traveling Salesman : Problem bei Beispiel 1 Problem 1: Wenn ein weiterer Knoten "Mainz" zwischen Bonn und Frankfurt aufgenommen wird, ist das TSP nicht lösbar. Problem 2: Wenn zusätzlich eine Verbindung Mainz Mannheim aufgenommen wird, gibt es mehrere Zyklen. Köln 5 Marburg 174 30 34 Bonn 0 104 Gießen 181 140 41 224 Mainz 9 71 6 Mannheim 96 7 106 3 66 1 2 Fulda 104 Frankfurt 136 88 4 Kassel 93 8 Würzburg Lösungen des TSP • Bis heute ist keine effiziente Lösung des TSP bekannt. Alle bekannten Lösungen sind von der Art : Allgemeiner TSP-Algorithmus: Erzeuge alle möglichen Touren; Berechne die Kosten für jede Tour; Wähle die kostengünstigsteTour aus. • Folgerung : Die Komplexität aller bekannten TSPAlgorithmen ist O(2N), wobei N die Anzahl der Kanten ist. Das heißt: Wenn wir eine einzige Kante hinzunehmen, verdoppelt sich der Aufwand zur Lösung des TSP! • Beispiel: Hinzunahme einer neuen Verbindung Mannheim-Würzburg im obenstehenden Graphen. Varianten des TSP • Eine Verallgemeinerung des TSP besteht darin, auf die Rückkehr zum Ausgangspunkt zu verzichten: - TSP (u,v): Finde einen einfachen Weg von Knoten u nach Knoten v, der alle Knoten von G enthält. • Offenbar stellt TSP(u,u) das ursprüngliche TSP-Problem dar. • Beispiel: TSP(Marburg, Gießen) ist (im ursprünglichen Graphen) eindeutig lösbar, TSP (Marburg, Köln) ist nicht lösbar, für TSP (Marburg, Würzburg) müssen im erweiterten Graphen mehrere mögliche Wege verglichen werden. • In einer weiteren Verallgemeinerung werden Mehrfachbesuche einzelner Knoten bis zu einer Schranke AriMax (= Maximalzahl zulässiger Besuche) zugelassen. - TSP (u,v,AriMax): Finde einen Weg von Knoten u nach Knoten v, der alle Knoten von G mindestens einmal und höchstens AriMax-mal enthält. • Mit dieser Verallgemeinerung (und geeignetem AriMax) ist das TSP immer lösbar - allerdings mit noch erheblich gesteigerten Aufwand! TSP-Algorithmus: Erläuterung (1) • Der folgende Algorithmus für das Problem TSP (u,v,AriMax) verfolgt eine Tiefendurchlauf-Strategie (depth first) mit Zurücksetzen (backtracking). Er verwendet - ein int-Array Marken zum Markieren der bereits besuchten Knoten, - ein int-Array MinWeg zum Speichern des (bisher) minimalen Weges - eine rekursive Prozedur besuche, die einen begonnenen Weg der Tiefe t an der Stelle k mit dem Ziel z fortsetzt. Ist der Weg noch keine vollständige Tour, und existiert zu k ein Nachbarknoten k', der noch weniger als AriMax-mal besucht wurde und bei dessen Besuch die gesamte Weglänge unterhalb der Weglängen-Schranke MinWegLen bleibt, wird der Weg bei k' mit Tiefe t+1 fortgesetzt (rekursiver Aufruf). TSP-Algorithmus: Erläuterung (2) - Weiter wird durch eine boolesche Funktion testeWeg geprüft, - ob k bereits der Zielknoten v ist, - ob alle Knoten besucht wurden. - Ist ein Weg gefunden, so wird seine Länge (AktWegLaenge) mit der von MinWeg (MinWegLen) verglichen. Falls er kürzer ist, wird er zum neuen MinWeg und seine Länge wird als neue MinWegLen festgehalten. Der Besuch wird abgebrochen. - Beim Abbruch eines Besuchs wird zum vorherigen Knoten zurückgekehrt (backtrack) und der nächste mit diesem verbundene Knoten besucht. Wurden alle Wege durchprobiert und mindestens eine Tour gefunden, so wird MinWeg, ansonsten eine Fehlermeldung ausgegeben. TSP-Algorithmus: Programm (1) class Graph{ ... private final int AriMax = 2; private int Schranke; private int WegeMax = 5*KnotenZahl; private int[] MinWeg = new int[WegeMax]; private int AktWegLaenge; ... private int wegLaenge(int[] Weg, int maxIndex){ int erg = 0; for (int w = 0; w < maxIndex; w++) erg += Kanten[Weg[w]][Weg[w+1]]; return erg; } TSP-Algorithmus: Programm (2) private boolean testeWeg(int[] Weg, int[] Marken, int maxIndex, int Ziel){ if (Weg[maxIndex] != Ziel) return false; int Max = KnotenZahl; if (maxIndex < (Max-1)) return false; for (int k = 0; k < Max; k++) if (Marken[k] == 0) return false; return true; } private void besuche(int k, int[] Weg, int maxIndex, int Ziel, int[] Marken){ int Max = KnotenZahl; maxIndex++; if (maxIndex >= (WegeMax-2)){ System.out.println("Der Weg wurde zu lang ..."); return; } ... TSP-Algorithmus: Programm (3) ... Weg[maxIndex] = k; int BisherigeLaenge = 0; if (maxIndex == 0) AktWegLaenge = 0; else { BisherigeLaenge = AktWegLaenge; AktWegLaenge += Kanten[Weg[maxIndex-1]][Weg[maxIndex]]; } Marken[k]++; if (AktWegLaenge < Schranke){ if (TesteWeg(Weg, Marken, maxIndex, Ziel )){ System.arraycopy(Weg, 0, MinWeg, 0, maxIndex+2); Schranke = AktWegLaenge; } else{ ... TSP-Algorithmus: Programm (4) ... for (int n = 0; n < Max; n++) if (Kanten[k][n] > 0) if (Marken[n] < maxB) Besuche(n, Weg, maxIndex, Ziel, Marken); } } Besuch wieder rückgängig machen: AktWegLaenge = BisherigeLaenge; Weg[maxIndex] = -1; Marken[k]--; } TSP-Algorithmus: Programm (5) Hauptprogramm: Aufruf z.B.: MeinGraph.TSP(0,1); void TSP(int u, int v){ int Max = KnotenZahl; //int s = 0; // Automatisches Abschätzung für Schranke //for (int x=0; x < Max; x++) //for (int y=0; y < Max; y++) s += Kanten[x][y]; // Bzw. Experimentelles Setzen der Schranke int s =370; Schranke = s; int[] Weg = new int[WegeMax]; for (int i = 0; i < WegeMax; i++) Weg[i] = -1; System.arraycopy(Weg, 0, MinWeg, 0, WegeMax); ... TSP-Algorithmus: Programm (6) ... int[] Marken = { 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0 }; int maxIndex = -1; long Zeit1 = System.currentTimeMillis(); besuche(u, Weg, maxIndex, v, Marken); long Zeit2 = System.currentTimeMillis(); System.out.println("LaufZeit: "+(Zeit2-Zeit1)); if ( s == Schranke){ System.out.println("Kein Weg gefunden !!!"); return; } ... TSP-Algorithmus: Programm (7) ... System.out.println("Länge des kürzesten Weges: " + Schranke); System.out.println("Der kürzeste Weg ist: "); int KnotenNr = MinWeg[0]; int WegIndex = 1; while (KnotenNr >= 0){ for (int i=0; i < (2*WegIndex+10); i++) System.out.print(' '); System.out.println(Knoten[KnotenNr]); KnotenNr = MinWeg[WegIndex]; WegIndex++; } } } TSP für Beispiel 2 (1) • TSP Ausgangsknoten: San Francisco Zielknoten: San Rafael Länge des kürzesten Weges: 342 Der kürzeste Weg ist: San Francisco Pacifica Half Moon Bay San Mateo Eine Lösung des TSP Palo Alto von San Francisco nach Santa Clara Scotts Valley San Rafael ist mit einem Santa Cruz einfachen Weg möglich. Watsonville San Jose Fremont Hayward Oakland Richmond San Rafael TSP für Beispiel 2 (1) • TSP Ausgangsknoten: San Francisco Zielknoten: San Mateo Länge des kürzesten Weges: 364 Der kürzeste Weg ist: San Francisco San Rafael Richmond Oakland San Francisco Eine Lösung des TSP Pacifica von San Francisco nach Half Moon Bay Santa Cruz San Mateo gelingt nur mit Scotts Valley Mehrfachbesuchen. Santa Cruz Watsonville San Jose Santa Clara Palo Alto Fremont Hayward San Mateo TSP-Algorithmus: Anmerkungen (1) • Die Suche nach einer Lösung des TSP gelingt bei 15 Städten in nicht messbarer Zeit, wenn wir höchstens einen Besuch für jede Stadt zulassen. • Wenn wir zulassen, dass jede Stadt bis zu zweimal besucht werden darf, wächst der Rechenaufwand ganz erheblich. • Die Zahl der zu generierenden Wege steigt in diesem Fall dramatisch an. Für die Suche nach der Lösung des ersten Beispiels wurden 483 Wege generiert, beim zweiten Beispiel waren 3.832.913 Wege erforderlich. • Dies liegt an einer Vielzahl von unsinnigen Doppelbesuchen, die der Algorithmus nicht als unsinnig erkennt. Man müsste ihm eine Hilfestellung geben, welche Städte einmal und welche zweimal besucht werden sollen. • Die oben genannte Anzahl der generierten Wege ergibt sich, wenn man die Schranke automatisch abschätzen lässt. Mit einer kleineren Schranke erniedrigt sich die Zahl der generierten Wege erheblich. TSP-Algorithmus: Anmerkungen (2) • Offensichtlich kann man sehr viel Laufzeit sparen, wenn man die Suche auf einfache Wege beschränkt. Trotzdem kann man nicht hoffen, umfangreiche Travelling Salesman Probleme mit diesem Programm berechnen zu können. • Die Laufzeit L ist nämlich exponentiell, also L=c×2N mit einer geeigneten Konstanten c. Dies ist leicht einzusehen, denn selbst wenn jeder Knoten nur zwei Nachbarn hätte, verdoppelt sich jedes Mal die Anzahl der möglichen Wege. • Unter der Annahme, die Laufzeit im Falle N=15 sei eine Millisekunde gewesen, wenn wir höchstens einen Besuch für jede Stadt zulassen, finden wir c= 1/30 000 000 • Im Falle eines Graphen mit N=40 Knoten müssen wir mit einer Laufzeit von etwa 240/30 000 000 ≈ 10004/30 000 000 ≈ 30 000 Sekunden rechnen. Mit einem 10fach schnelleren Rechner könnten wir zwar die Aufgabe in nur 3000 Sekunden erledigen. Wenn wir dann allerdings die Zahl der Städte um zehn erhöhen, ergibt sich sofort eine tausendmal größere Laufzeit. • Aus diesen Gründen kann eine punktuelle Optimierung des Algorithmus keinen nennenswerten Einfluss auf die Laufzeit haben, solange nicht mindestens ein polynomialer Algorithmus gefunden wird – doch das ist höchst unwahrscheinlich. Literatur • Die Folien zum Traveling Salesman Problem wurden aus der Vorlesung aus der Vorlesung „Informatik II a, SS 2002“ von Prof. Sommer (Uni-Marburg) übernommen