[Teilweise Gelöst]Das Problem der kürzesten Wege

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

Schönen guten Tag,

Ich wende mich heute mit einem Problem an euch, zu lösen dessen ich nicht im Stande bin. Es handelt sich um ein einfaches 'kürzeste Wege Problem'; dachte ich. Es geht um einen ungerichteten Graphen mit 17 Knoten und 32 ungewichteten Kanten. Dieser repräsentiert klassisch Wege und Kreuzungen (bzw. Orte). Es gibt nun jeweils einen ausgewiesenen Start- und Endknoten (wobei dies auch Später ein Knoten sein kann; Stichwort - Rundreise)

Mein Ziel ist es nun den kürzesten Weg zwischen Start und Ziel zu finden. Soweit kein Problem, Dijkstra nimmt sich dieser gerne an. Jedoch habe ich eine zusätzliche Bedingung, die mir Kopfzerbrechen bereitet. Ich möchte nämlich auf dem Weg noch an (über) ganz bestimmte Knoten vorbeikommen (besuchen). Das soll heißen, dass ich eine kürzeste Route möchte die noch die Knoten (3,7,10,13) enthält.

Der kürzeste Wege erster Streich:
Mein erster Versuch bestand nun darin, ausgehend vom Startknoten, jeweils zu dem zu gehen der am nächsten gelegen ist. Ich habe also eine Liste (zlist) mit allen Zwischenstopps. Von Start aus berechne ich alle Entfernungen zu den Punkten in zlist, gehen dann zu dem mit dem kürzesten Weg und streiche jenen dann aus der zlist. Was sich zunächst gut anhörte hatte nur mäßigen Erfolg. Die Route sah einigermaßen gut aus, aber in bestimmten Konstellationen war es auch einfach nicht zu gebrauchen; die Route war ein einziges hin und her. Das ist nicht ganz unverständlich, wenn man bedenkt, dass ich für das Problem immer nur ein lokales Minima suche, obschon ein globales Minima gesucht ist - diese beiden koinzidieren leider nicht immer.
Bild
Man kann hier leicht sehen, dass der Algorithmus den "Fehler" macht und am Anfang vom Startknoten E1 zu Knoten 7 geht. Aus lokaler Sicht (und für die Art des Algos) ist das natürlich Richtig, nur ist es für meine Zwecke denkbar schlecht. Danach Sucht er sich wieder den nächsten Punkt über 13 und 10 und muss dann wieder zurück zu Punkt 3 um dann abermals zurück zum Zielpunkt E2 zu gehen. "Klüger" wäre es in diesem Fall zum weiter entfernteren Knoten 3 zu gehen ...
Bild

Der kürzeste Wege zweiter Streich:
Unzufrieden mit dem Ergebnis habe ich dann versucht das Problem anders anzugehen. Ausgehend von der Schwäche der "lokalen Minima", musste nun was globaleres her. Ich habe also eine Liste (zlist) mit allen Zwischenstopps. Nun berechne ich zu allen Paaren in zlist die kürzesten Wege untereinander - Dijkstra übernehmen Sie. Diese Wege der Paarungen speichere ich mir in einem Dict (pdict) mit der Form pdict[startknoten][zielknoten] = [startknoten, knoten1, knoten2, ... , zielknoten]. Also jeweils die kürzesten Wege zwischen zwei Punkte in zlist (Den Punkten die ich unbedingt besuchen will). Nun Berechne ich mir zu allen Permutationen von zlist (also alle möglichen Wege) wer denn nun der kürzeste ist. Beispielsweise die Permutation [E1, 3, 7, 10, 13, E2]. Hierzu nehme ich einfach pdict[E1][3] und füge die Teilstrecke dem gesamt Weg hinzu, weiter mit pdict[3][7] ... usw und vergleiche dann die Länge der gesamt Wege.

Das funktioniert auch alles wunderbar und das Ergebnis ist fast identisch mit dem was ich mir ersonnen hatte.
Bild

Leider hat die Sache einen entscheidenen Haken: " Nun Berechne ich mir zu allen Permutationen von zlist". Leider hat das ganze ein O(n!) für alle Permutationen und da komme ich bei 9 Elementen in zlist schon auf knapp 4 Sekunden, 10 Elemente knapp 40 Sekunden und bei 11 sind es fast schon 10 Minuten.

Kennt jemand vielleicht eine Methode das Problem schneller zu lösen als O(n!)? Habe ich vielleicht Problemen mit meinem jetzigen Code, dass ich so schnell schon an die Grenze stoße - ich meine 9! ist gerade mal 362880 und dafür schon 4 Sekunden? Macht es sinn Code zu Posten oder bewegen wir uns hier in einem "normalen" Ramen? Ich denke das Hauptproblem liegt nicht an unoptimiertem Code denn des aufwendigen Algorithmus'es

Was ich schon versucht habe, ist eine Art Abschätzung wann ich das "Streckenbilden" abbrechen kann. Ich lasse den Algorithmus aus dem "ersten Streich" laufen (der ungemein schneller ist) und habe damit eine obere Schranke. Wenn ich also eine Strecke bilde die schon genauso lang ist breche ich ab. Genause wie ich mir die Momentan kürzeste Strecke merke (auch als obere Schranke) und abbreche wenn diese Überschritten wird. Das alles gibt aber maximal so um die 2% zuwachs an Geschwindigkeit.


Über Denkanregungen in dieser Hinsicht würde ich mich freuen.

hochachtungsvoll Lordnaikon :)
Zuletzt geändert von lordnaikon am Sonntag 12. Juni 2011, 15:23, insgesamt 1-mal geändert.
deets

http://de.wikipedia.org/wiki/A*-Algorithmus

Sollte dir Hinweise genug geben. Es finden sich sicherlich auch Implementation in Python, falls es dir nur um eine Loesung, und nicht um's loesen selbst geht ;)
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

Ich habe mich in der Tat bisher nur wenig mit dem A* auseinander gesetzt. Deshalb ist mir auch noch nicht so recht klar, wie er mir helfen soll. Das liegt wohl auch am fehlenden Verständnis über jenen selbst. Mir ist nicht so richtig klar wie ich da eine passende Heuristik anwenden kann. Soll ich Knoten die ich besuchen will ein hohen Gewicht geben und jenen anderen ein niedriges? Kannst du deine Gedanken diesbezüglich noch etwas ausführen, derweil verbringe ich etwas Zeit mit lesen über A*.
deets

Entschuldige, ich habe deine Beitrag nicht wirklich aufmerksam genug gelesen, und dein Randbedingungen darum nicht beachtet.

Trotzdem koennte dir A* helfen, denn du koenntest zB die euklidische Entfernung zum naechsten deiner Zwischenstationen als Kostenfunktion benutzen. Damit kommst du dann segmentweise voran. Wie gut diese heuristik gegenueber einer optimalen Loesung ist kann ich aber nicht aus dem Stand so einschaetzen.
lunar

@deets: Wie soll man denn in einem allgemeinen Graphen euklidische Distanzen berechnen?

@lordnaikon: Der Quelltext schadet sicher nicht, schließlich hängt die Effizienz des Dijkstra-Algorithmus auch davon ab, wie man diesen implementiert. Ansonsten glaube ich nicht, dass Du eine optimale Lösung algorithmisch effizienter finden kannst (zumindest nicht in einem allgemeinen Graphen), denn intuitiv ähnelt dieses Problem irgendwie dem TSP, was bekanntlich NP-äquivalent ist.
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

@deets:
Leider kann ich keine Aussage über längen von Wegstrecken treffen die über die Anzahl an Knoten hinaus geht. Ich kann also nicht sagen Knoten 1 und 9 liegen 200 Meter auseinander; ich kann nur sagen es liegen 2 Knoten dazwischen (was der Graph halt hergibt).

@lunar:
Ich kann dir versichern, dass der Codeabschnitt über Dijkstra nichts zur Laufzeit beiträgt. Aus der Erinnerung ist es für den Dijkstrateil 0,0xxx oder 0,xxx Sekunden bei 40 Sekunden für den Permutationsteil. TSP hatte ich auch schon die ganze Zeit, wehmütig dessen Aufwandes, im Hinterkopf.


Ich bin auch etwas verwundert darüber, wie wenig ich darüber bei google (beim schnellen Suchen) gefunden habe :K Ich finde das Problem ist nicht all zu exotisch oder? :wink:
deets

@lunar

Joa, man sollte verkatert nicht zu zu komplizierten Sachverhalten Meinungen ausbilden :oops:
lunar

@deets: Gute Besserung :)

@lordnaikon: Google ist eben nicht die Antwort auf alle Fragen. Es gibt vieles, was Google nicht findet. Als allgemeine Suchmaschine ist Google für spezifische nicht-triviale Probleme nicht unbedingt die sinnvollste Wahl. Je spezifischer, je „wissenschaftlicher“ eine Fragestellung, desto weniger verwertbare Resultate bietet Google.

Eine gezielte Suche in themenspezifischen Quellen ist meist sinnvoller. StackOverflow ist beispielsweise eine gute Quelle bei Programmierproblemen aller Art, die schnell zum Ziel führt. Ansonsten ist auch die klassische Literaturrecherche nicht zu verachten. Einführende Bücher zum Thema Graphentheorie mit guten Literaturverzeichnis sollten einen entsprechenden Fundus an wissenschaftlichen Quellen zum Thema kürzeste Pfade eröffnen, in dem man dann gezielt nach Lösungen suchen kann. Dafür musst Du Dich nicht mal in die Bibliothek begeben, Google Scholar oder ähnliche wissenschaftliche Suchmaschinen erzielen oft recht brauchbare Resultate.

Eine kurze Suche nach "constrained shortest path" bei Google Scholar liefert beispielsweise eine Dissertation unter dem Titel Constrained shortest path and related problems, die unter anderem bestätigt, dass die Suche eines kürzesten Wegs unter Nebenbedingungen (e.g. Besuch bestimmter Knoten) ein hartes Problem ist.

Du musst Dich wohl vom Ziel einer optimalen Lösung verabschieden, und entweder heuristische Verfahren nutzen, oder nur lokal optimieren. Vielleicht ist es sinnvoll, Anleihen bei Routing-Algorithmen für Netzwerke zu machen, die Problemstellung, möglichst optimale Wege durch bestimmte Knoten zu finden, existiert dort jedenfalls in ähnlicher Form.
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

@lunar: Ja, so habe ich google natürlich auch nicht gesehen und ich weiß das du das weißt das wir wissen ... ähh egal. :roll: Was ich sagen wollte war: Intuitiv kam mir das Problem als solches nicht so ungewöhnlich vor; nicht ungewöhnlich genug, als dass es nicht schon tausendfach in Hobbyspieleprogrammierforen gefragt wurde - wie lasse ich die KI möglichst schnell alle Medipacks aufsammeln/ Schalter betätigen ... usw. Danke erstmal für die Hinweise! Jetzt habe ich wenigstens mal so einigermaßen einen Überblick. Witzig, dass meine Projekte sich öfter mal an graphentheoretischen Sachen aufhängen. Ich erinnere mich da noch an eine Diskussion mit, unter anderem, BlackJack an dieser Stelle was mich zwang auf proprietäre Software zu setzen, weil ich dem Problem nicht habhaft werden konnte. Dann werd ich mich erstmal wieder der Literatur widmen ... eigentlich wollte ich für das Problem gar nicht so viel Aufwand betreiben :? ... wer hätte das gedacht :K
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

Ich habe das Problem nun, für meinen speziellen Fall, gelöst. Leider ist die Lösung natürlich nicht allgemein anwendbar (wäre auch ein bisschen happig, für nur eine Nacht drüber schlafen, so ein Problem allgemein zu lösen :roll: ), vielleicht hilft es aber dem einen oder anderen mit einem ähnlichen Problem. Darum verfasse ich es hier mal aus dokumentarischen Gründen.

Das Problem war, in meinem speziellen Fall, dass Zwischenstopps >9 erhebliche Zeit in Anspruch genommen haben - 9 Zwischenstopps 4 Sekunden, 10 Zwischenstopps 40 Sekunden. Heut Morgen ist mir dann "divide and conquer" in den Sinn gekommen. Dabei ist mir aufgefallen, dass sich mein Graph an einer Stelle verjüngt nämlich an den Knoten 7 und 8. Was wäre, wenn ich meinen Graphen an genau dieser stelle Teile?
Bild
Gesagt getan. Was ich nun machen muss ist Teilrouten zu berechnen. Im oberen Teil (Graph1) ist das neue Ziel entweder 7 oder 8 (weil ich da lang muss). Im unteren Teil (Graph2) ist der neue Startknoten 7 oder 8. Jetzt berechne Ich für Graph1 beide Routen und für Graph2. Macht zusammen 4 Routen berechnen. Der Clou an der Sache ist: selbst wenn ich alle Knoten wähle habe ich für den Graph1 und Graph2 jeweils nur 9 Zwischenstopps (Knoten 10 ausgenommen, der ist dann ein Spezialfall, den muss man nicht berechnen). So habe ich eine Obere Schranke von 4 x 9Zwischenstopss = 16 Sekunden - wenn alles ausgewählt wurde!
Danach guck ich noch schnell, welche der Kombinationen aus den vier Routen die kürzeste ist. (Ehrlicherweise muss man noch den Fall betrachten, dass Punkt 7 und 8 auch besucht werden wollen, dass hab ich aber außen vor gelassen)

Hinzukommt noch, dass ich meinen Code noch etwas optimieren konnte, line_profiler war mir da ein gutes Helferlein. Zum Schluss noch Cython drüber laufen lassen und siehe da für alle 17 Zwischenstopps, also worstcase, 0.32 Sekunden. Für einen "normalen" Problemfall mit 11 Zwischenstopps ergibt sich eine Laufzeit von 0.028 Sekunden - gar nicht mal so schlecht, wie ich Finde
Bild

Ich hoffe, dass das Geschriebene für Irgendjemanden Irgendwann mal hilfreich sein könne.

Für weitere Hinweise hätte ich aber dennoch ein offenes Ohr!

mfg Lordnaikon
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Ich kann leider nicht viel praktisches zu deinem Problem beitragen, als Geograf kenne ich aber die Problematik des kürzesten Weges zwischen zwei Punkten in einem Netzwerk. Da gibt es den Travelling Salesman, der auf einer Reihe von vorgegebenen Knoten in einem Netzwerk die kürzeste Route sucht. Normalerweise ist da Start- und Endpunkt gleich, aber vielleicht gibt dir ja die Implementierung noch Hinweise.
Hier habe ich noch einen Link, der verschiedene Dijkstra und verwandte Algorithmen behandelt. Gute Stichworte für die Suchmaschine sind GIS, shortest path, network analysis.

Ich hoffe, das hilft dir vielleicht noch weiter

Frank
Antworten