Prof. Thomas Richter Institut für Analysis und Numerik Otto-von-Guericke-Universität Magdeburg [email protected] 8. Juni 2017 Material zur Vorlesung Algorithmische Mathematik II am 08.06.2017 Problem des Handlungsreisenden 1 Problem des Handlungsreisenden Wir formulieren zwei Probleme Folie 1 Gegeben sei ein zusammenhängender, gewichteter Graph G = (V, E, fE ) sowie eine Menge von Knoten S = {s1 , . . . , sk ∈ V}. Gesucht ist der Pfad e em−1 e 1 2 x1 −→ x2 −→ · · · −−−→ xm welcher alle Knoten si ∈ S in beliebiger Reihenfolge besucht und kleinstes summiertes Kantengewicht hat m−1 X fE (ei ) → min i=1 Folie 2 Gegeben sei ein vollständiger (d.h. zu x, y ∈ V existiert die Kante e = (x, y) ∈ E), gewichteter Graph G = (V, E, fE ). Gesucht ist der Kreis, welcher alle Knoten des Graphen genau einmal besucht und dabei kleinstes summiertes Kantengewicht hat. n X fE (ei ) → min i=1 Wir werden im Folgenden ausschließlich dieses Problem bei Betrachtung eines vollständigen Graphen untersuchen. Die Speicherung eines solchen Graphen ist einfach. Da jeder Knoten mit jedem anderen Knoten verbunden ist speichern wir lediglich die n2 Gewichte in einer Matrix A ∈ Rn×n . Wir gehen weiter davon aus, dass diese Matrix symmetrisch ist. 1 Folie 3 Definition 1 (Symmetrisches Handlungsreisendenproblem). Es sei G = (V, E, fE ) ein vollständiger gewichteter Graph mit n = |V| Knoten und Gewichtsmatrix A ∈ Rn×n . Im Fall fE (e) = fE (e 0 ) für Kanten e = (x, y) und e 0 = (y, x), d.h. im Fall A = AT heißt das Handlungsreisendenproblem symmetrisch. Definition 2 (Metrisches Handlungsreisendenproblem). Es sei G = (V, E, fE ) ein vollständiger gewichteter Graph mit n = |V| Knoten. Falls für drei Knoten x, y, z ∈ V und Kanten e = (x, z), e1 = (x, y) und e2 = (y, z) stets gilt fE (e) 6 fE (e1 ) + fE (e2 ), also ∀i, j, k ∈ {1, . . . n} Aik 6 Aij + Ajk so heißt das Handlungsreisendenproblem metrisch. In der Anwendung treten sowohl symmetrische als auch unsymmetrische Probleme auf. Die Routenplanung kann z.B. Einbahnstraßen, Baustellen, unterschiedliche Tempolimits berücksichtigen. Metrische Probleme treten z.B. beim Bohren von Löchern in Leiterplatten auf (eine Anzahl an Löchern in vorgegebener Position muss möglichst schnell gebohrt werden, im Anschluss muss der Bohrer wieder zurück). Auch bei der Navigation scheint es sich um ein metrisches Problem zu handeln. Umwege können sich aber durchaus lohnen, je nachdem welches Verkehrsmittel, welche Straße verwendet wird. 2 Beim symmetrischen Problem muss nur die Hälfte der Kanten gespeichert werden. Wir beachten diese Optimierung hier jedoch nicht und speichern einen vollständigen metrischen Graphen in folgender einfacher Struktur. Folie 4 Algorithmus 1: Vollständiger, gewichteter Graph 1 2 3 4 c l a s s VGGraph { public : v e c t o r < v e c t o r <double> > A; 5 VGGraph ( ) { } VGGraph ( i n t n ) { A. r e s i z e ( n , v e c t o r <double> ( n , − 1 . 0 ) ) ; } 6 7 8 9 10 11 12 void I n i t Z u f a l l ( i n t n ) { A. r e s i z e ( n , v e c t o r <double> ( n , − 1 . 0 ) ) ; 13 14 15 16 Zufallszahlengenerator Z; v e c t o r <double> X( n ) ,Y( n ) ; / / f ü r m e t r i s c h e n Graphen int MI = 1 0 0 0 0 0 0 ; double MX = s q r t ( n ) ; f o r ( i n t i = 0 ; i <n;++ i ) { X[ i ] = 1 . 0 /MI∗Z ( 0 , MI ) ∗MX; Y[ i ] = 1 . 0 /MI∗Z ( 0 , MI ) ∗MX; } f o r ( i n t i = 0 ; i <n;++ i ) f o r ( i n t j = i ; j <n;++ j ) { double d=max ( f a b s (X[ i ]−X[ j ] ) , f a b s (Y[ i ]−Y[ j ] ) ) ; A[ i ] [ j ]=A[ j ] [ i ]=d ; } 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 } 32 33 }; 3 2 Exakte Berechnung der Lösung Es ist nun G = (V, E, fE ) ein vollständiger Graph und wir suchen einen Kreis, der jeden Knoten genau einmal besucht (und wieder am Startpunkt ankommt). Die Angabe einer Lösung kann somit durch Angabe der Reihenfolge der durchlaufenden Punkte angegeben werden. Folie 5 Satz 1 (Startpunkt). Es sei G = (V, E, fE ) ein vollständiger symmetrisch gewichteter Graph und x1 → x2 → · · · → xn → x1 eine optimale Lösung des Handlungsreisendenproblems. Dann ergibt sich eine weitere optimale Lösung für jeden beliebigen Startpunkt als xk → xk+1 → · · · xn → x1 → · · · → xk−1 → xk . Jede dieser Lösungen ist auch in umgekehrter Reihenfolge optimal. Beweis. Alle diese Lösungen beinhalten die Kanten (xk , xk+1 ) für k = 1, . . . , n − 1 sowie die Kante (xn , x1 ), gegebenenfalls in unterschiedlicher Reihenfolge. Das summierte Kantengewicht ist stets gleich. Da fE ((x, y)) = fE ((y, x)) so ist auch in umgekehrter Reihenfolge ein Kreis mit gleichem Gewicht gegeben. Die Schwierigkeit des Handlungsreisendenproblems liegt in der sehr großen Anzahl an verschiedenen Kreisen. Folie 6 Satz 2 (Anzahl an Lösungen). Es sei G = (V, E, fE ) ein vollständiger symmetrisch gewichteter Graph mit n = |V| Knoten. Es gibt (n − 1)! 2 potentiell verschiedene Kreise. Beweis. Wir haben bereits gezeigt, dass die Wahl des Startknotens nicht relevant ist und wählen daher ohne Einschränkung x1 = 1. 4 Es bleiben n − 1 Möglichkeiten zur Wahl von x2 , n − 2 zur Wahl von x3 usw. Insgesamt ergeben sich auf diese Weise (n−1)! verschiedene Kreise. Jeder von diesen Kreisen wird in beide Richtungen durchlaufen. Mit diesem Ergebnis erhalten wir sofort eine “Lösung” des Problems. Folie 7 Satz 3. Es sei G = (V, E, fE ) ein vollständiger gewichteter symmetrischer Graph mit n = |V| Knoten. Das Handlungsreisendenproblem lässt sich in O(n!) Operationen lösen. Als Beweis geben wir einen Algorithmus an. Folie 8 Algorithmus 2: Exakte Lösung des Handlungsreisendenproblems 1 2 3 4 Setze f = ∞ Für a l l e Permutationen von (1, x2 , . . . , xn ) ∈ (1, 2, . . . , n) Pn−1 Berechne f = fE (xn , x1 ) + j=1 fE ((xj , xj+1 )) F a l l s f < fmin merke Permutation Beweis. Es gibt (n − 1)!/2 relevante Permutationen. Für jede dieser Permutationen lässt sich die Kantensumme in O(n) Operationen berechnen. Hiermit ergibt sich der entsprechende Aufwand. 5 Folie 9 Definition 3 (Lexikographische Ordnung). Für zwei Listen l = l1 , . . . , ln und r = r1 , . . . , rn definieren wir li = ri i = 1, . . . , k − 1 6 n l<r ⇔ lk < rk Auf diese Weise erzeugen wir eine Ordnung auf den Permutationen, z.B. (1, 2, 3) < (1, 3, 2) < (2, 1, 3) < (2, 3, 1) < (3, 1, 2) < (3, 2, 1) Idee für Algorithmus: • Gegeben Permutation p1 , . . . , pn • Bestimme nächste Permutation bzgl. der lexikographischen Ordnung • Vorteil: es gibt eine erste (kleinste) und letzte (größte) Permutation 6 In C++ ist dieses Vefahren implementiert und kann einfach verwendet werden Folie 10 1 2 3 # include < v e c t o r > # include <iostream > # include <algorithm > / / h i e r s i n d P e r m u t a t i o n e n d e f i n i e r t 4 5 using namespace s t d ; 6 7 8 9 10 11 i n t main ( ) { vector <int > a ; f o r ( i n t i = 0 ; i <3;++ i ) a . push_back ( i ) ; 12 do { cout << a [ 0 ] << " ␣ " << a [ 1 ] << " ␣ " << a [ 2 ] << endl ; } while ( next_permutation ( a . begin ( ) , a . end ( ) ) ) ; 13 14 15 cout << endl ; 16 17 do { cout << a [ 0 ] << " ␣ " << a [ 1 ] << " ␣ " << a [ 2 ] << endl ; } while ( next_permutation ( a . begin ( ) +1 , a . end ( ) ) ) ; 18 19 20 cout << endl ; a [0]=7; a [1]=2; a [2]=0; 21 22 23 24 25 do { cout << a [ 0 ] << " ␣ " << a [ 1 ] << " ␣ " << a [ 2 ] << endl ; } while ( next_permutation ( a . begin ( ) , a . end ( ) ) ) ; 26 27 28 return 0 ; 29 30 } Folie 11 Iteratoren Für einen Vektor vector<int> a geben die Funktionen a.begin() und a.end() Iteratoren zurück. Ein Iterator ist im wesentliche ein Zeiger auf eine Speicherstelle. a.begin() ,→ zeigt auf den Speicher, an dem der erste Eintrag im Vektor steht. a.end() auf die Speicherstelle hinter den letzten Eintrag. Mit Iteratoren kann gerechnet werden. a.begin()+1 zeigt auf die Speicherstelle des zweiten Eintrags, a.end()−1 auf den letzten Eintrag. 7 Folie 12 Die Laufzeit steigt enorm schnell: n Minimale Länge Zeit n Minimale Länge Zeit 3 4 5 6 7 8 1.52839 2.02879 2.42862 2.54813 3.03082 3.03082 1.97 · 10−6 2.40 · 10−6 3.29 · 10−6 5.21 · 10−6 1.38 · 10−5 8.49 · 10−5 9 10 11 12 13 14 3.10372 3.10648 3.12038 3.19257 3.21485 3.21485 5.77 · 10−4 5.42 · 10−3 5.40 · 10−2 6.30 · 10−1 8.22 · 10−0 1.38 · 10+2 Die Länge des Weges steigt hier monoton, da dem Graphen jedes mal nur ein neuer Knoten hinzugefügt wird. Die alten Knoten bleiben jeweils bestehen. Es existieren bessere exakte Algorithmen. Diese kommen alle aus dem Gebiet der linearen Optimierung. Im allgemeinen Fall ist das Problem jedoch nicht effizient lösbar. Wir sind genauer. Folie 13 Satz 4 (Komplexität des Handlungsreisendenproblems). Es sei G = (V, E, fE ) ein vollständiger, symmetrischer, metrischer gewichteter Graph. Für das Handlungsreisendenproblem gibt es keinen allgemeinen Algorithmus, welcher das Problem stets in polynomialer Laufzeit löst. Dies bedeutet, dass es kein Polynom p ∈ P gibt p(x) = α0 + α1 x + · · · + αK xK mit K ∈ N beliebig aber fest, so dass das Problem stets (also bei beliebig ungünstiger Verteilung der Gewichte) im Aufwand A(n) = O(p(n)) gelöst werden kann. Das oben angesprochene triviale Verfahren hat nicht polynomielle Laufzeit, denn n! ist kein Polynom in n. 8 3 Approximierung der Lösung Es existieren verschiedene effiziente Verfahren, welche die Lösung des Handlungsreisendenproblems approximieren. Folie 14 Ein einfaches heuristisches Vorgehen ergibt sich durch folgende Strategie: • Wir starten in einem beliebigen Knoten, ohne Einschränkung x1 = 1. • In jedem Schritt wählen wir als nächstes den Knoten mit minimalem Abstand unter denjenigen, die wir noch nicht besucht haben. • Zum Abschluss fügen wir die Kante zum Startknoten hinzu. Satz 5 (Nächster-Nachbar). Der Nächste-Nachbar Algorithmus lässt sich in quadratischer Laufzeit O(n2 ) umsetzen. Beweis. Von x1 = 1 werden in n − 1 Schritten die weiteren Knoten hinzugefügt. Die Suche nach dem Minimum benötigt ohne Verwendung spezieller Datenstrukturen jeweils n Operationen. 9