Das Problem des Handlungsreisenden 0 Gliederung 1 Problemstellung 2 Historischer Hintergrund 3 Priorität und Komplexität des Problems 3.1 Anwendung im Alltag 3.2 Mögliche Erweiterungen 4 Lösung des Problems 4.1 Theoretischer Lösungsansatz 4.2 Umsetzung in Delphi 1 Problemstellung Das Problem des Handlungsreisenden Es existieren mehrere Punkte, die sich in ihrer geographischen Lage voneinander unterscheiden, wobei nur einige dieser Punkte mit direkten Wegen verbunden sind (Auch Einbahnstraßen sind möglich). Man sucht einen Algorithmus, der jeweils die kürzeste Verbindung zwischen mindestens zwei Punkten ausdrückt. Optimierungsproblem -> Es ist nicht schwierig eine gute Lösung zu finden. Die optimale zu finden gestaltet sich allerdings als eine Herausforderung! => Es handelt sich bei dem Problem des Handlungsreisenden demnach um eine Erweiterung des Labyrinthproblems. Aus Problemstellung ergeben sich zwei verschiedene Ausprägungen des Problems des Handlungsreisenden. Einerseits kann man die kürzeste Route zwischen zwei Knotenpunkten berechnen. Andererseits kann man die kürzeste Route berechnen, die durch mehrere Knotenpunkte geht. Im Folgenden werde ich beide Problemausprägungen erläutern und zuletzt dein Lösungsansatz und die Berechnung der kürzesten Route zwischen zwei Punkten aufzeigen. 2 Historischer Hintergrund - Das Problem tauchte höchst wahrscheinlich 1832 das erste Mal auf. Aus dieser Zeit ist ein Buch bekannt ( Der Handlungsreisende ) in dem das Problem zwar erwähnt, aber nicht wissenschaftlich untersucht wird. - Wissenschaftlich/ mathematisch untersucht wurde das Problem nachweisbar erst 1930 von Karl Menger. - Der US-amerikanische Mathematiker Hassler Whitney, der an der Princeton University in New Jersey (USA) arbeitete, prägte den Begriff Traveling Salesman Problem (Princeton ist eine der angesehensten Universitäten auf der ganzen Welt!). - Mit dem Problem des Handlungsreisenden beschäftigen sich Forscher noch heute intensiv, da bis jetzt lediglich Algorithmen gefunden wurden, deren Anzahl der Aufrufe, die direkt mit der Rechenzeit zusammenhängen, mit der Anzahl der 1
Knotenpunkten exponentiell ansteiget. Gesucht wird ein Algorithmus, bei dem dieser Vorgang linear verläuft. - Während das Finden der kürzesten Verbindung vor 50 Jahren mit 30 Knotenpunkten schon eine Herausforderung war, ist es heutzutage Forschern gelungen die kürzeste Verbindung zwischen mehreren Millionen Knotenpunkten zu finden, die nachweisbar weniger als 1% von der optimalen Verbindung abweicht. 3.1 Anwendung im Alltag Das Problem des Handlungsreisenden ist nicht nur in der Informatik von großer Bedeutung, sondern in fast allen Bereichen des Alltags. Hier einige Beispiele: - Ein Handlungsreisender hat die Aufgabe mehrere Orte anzufahren, wobei er selbst von der Wahl der kürzesten Route profitiert. Der Handlungsreisende kann verschiedene Identitäten annehmen z.b.: - Auslieferungsservice der Post - Postbote - ADAC-Pannendienst - Fluggesellschaft - Klempner - Handwerker - Kundendienst eines Unternehmens - Busgesellschaft - Bahn - Kreuzfahrtgesellschaft - Winterstreudienste - Taxigesellschaft Dies sind möglicherweise die nahe liegensten Anwendungsgebiete, doch auf keinen Fall die einzigen. Bei diesen Beispielen beherrscht ein Navigationssystem die Aufgabe, die kürzeste bzw. schnellste Route zu berechnen. - In der Elektroindustrie sucht man die kürzeste Verbindung verschiedener Lötpunkte, um nicht nur Zeit, Rohstoffe und Geld, sondern auch Platz zu sparen(-> Mikrochips, Handys, Computer, Laptops, Digitalkameras, usw.) - In der Automobilindustrie strebt man danach die Produktionsroboter so zu programmieren, dass sie die kürzeste Route zum Auftragen der Schweißpunke wählen um Zeit, Strom und somit auch Geld zu sparen. - Die Konstruktion von Bahnhöfen, Flughäfen, Museen und Stadien kann unter Beachtung des Problems des Handlungsreisenden erleichtert werden, wenn man die Bewegung der Menschenmassen voraussehen kann. - Da unsere Gesellschaft immer mehr zu einer High-Tech-Gesellschaft tendiert wird die Priorität des Problems des Handlungsreisenden in den kommenden Jahren und Jahrzehnten aus Gründen der immer wieder neu entwickelten Anwendungsgebiete steigen. 2
3.2 Mögliche Erweiterungen Man kann die Komplexität des Problems des Handlungsreisenden nahezu beliebig steigern. Es folgen einige Vorschläge, die größtenteils kombinierbar sind: - Man hat mehrere Handlungsreisende. - Man plant Fahrerwechsel ein. - Man achtet nicht nur auf die Länge der Streckenabschnitte, sondern auch auf die Geschwindigkeiten, die gefahren werden können. - (Berufs-)Verkehrsbedingte Engpässe umfahren. - Ausgelastete Straßen/ Staus umfahren. - Zusätzlicher Zwischenstopp zum Tanken einfügen. - Mautpflichtige Straßenabschnitte umfahren. 4 Lösung des Problems Gesucht ist die kürzeste Entfernung zwischen zwei Knotenpunkten. 4.1Theoretischer Lösungsansatz Der Lösungsansatz wird an einem Beispiel gezeigt. Die Buchstaben A bis G stellen Städte dar. Die Striche symbolisieren Verbindungsstraßen und die Zahlen geben ihre Länge in Kilometern an. Der orange gefärbte Pfeil weist auf eine Einbahnstraße hin. Eine mögliche Fragestellung könnte lauten: Welche Routen existieren, wenn man A als Ausgangs- und E als Zielstadt annimmt, und welche ist der kürzeste? Zur Übersicht habe ich alle Möglichkeiten in einem Baumdiagramm dargestellt: 3
Die Zahlen sind in Kilometer angegeben, wobei sich die grünen Zahlen auf die Verbindungsstrecken zwischen zwei nebeneinander liegenden Städten und sich die roten Zahlen auf die gesamte Streckt von A nach E beziehen. An erster Stelle im Baumdiagramm steht die Ausgangsstadt (A). Nun zeichnet man alle Möglichkeiten ein, wie man fahren kann. Immer wenn man auf eine Kreuzung trifft, wie zum Beispiel bei Punkt B, spaltet sich der Ast im Baumdiagramm und setzen sich unabhängig voneinander fort. In einem Programm sähe es so aus, dass der Algorithmus erst einen Weg vollständig zeigen würde. Wenn er bei dem Ziel E angekommen ist, würde er zu der vorigen Kreuzung zurückkehren und den Weg in eine andere Straße fortsetzen. Dieses Verfahren würde er solange durchführen, bis es alle Routen gefunden hätte. Man nennt es Backtracking. Auf diese Weise kommt man zu dem Ergebnis, dass die kürzeste Route von A nach E 90 Kilometer lang ist, das man auch der Skizze und dem Baumdiagramm entnehmen kann. Bei diesem Verfahren stößt man jedoch auf ein Problem, dass in dem oben stehenden Baumdiagramm mit drei orange gefärbten Punkten gekennzeichnet ist. Das Problem besteht darin, dass nicht alle Wege zum Zielpunkt E führen. Der Algorithmus würde in diesem Fall wegen einer Endlosschleife nie enden. Aus diesem Grund benötigt man für den eben beschriebenen Fall eine Abbruchbedingung. Das Problem würde ich folgendermaßen lösen: - Der Weg darf niemals zweimal durch denselben Punkt gehen. In diesem Fall müsste der Algorithmus zurück zur vorigen Kreuzung gehen und einen anderen Weg wählen. - Wenn der Algorithmus das erste Mal auf den Zielpunkt trifft merkt er sich die Länge dieser Route als kürzeste Route. Wenn er einen Weg wählt der länger als die kürzeste Route ist kehrt er automatisch zur vorigen Kreuzung zurück und nimmt einen anderen Weg. Wenn er erneut auf Den Zielpunkt trifft und diese neue Route kürzer ist als die kürzeste Route, dann merkt er sich die neue Route als die kürzeste Route. Diese Schritte wiederholt er so oft, bis er alle Möglichkeiten versucht hat. Dann bricht er (die Suche) ab und hat sich die wirklich kürzeste Route automatisch gemerkt. Der entscheidende Vorteil dieser Erweiterung ist, dass die Laufzeit dieses Algorithmus drastisch reduziert wird, da er nicht mehr alle Möglichkeiten komplett durchspielt, sondern nur noch die, dessen Strecke kürzer ist als die der bis jetzt gefundene kürzeste Verbindungsstrecke des Anfangs- mit dem Zielpunkt. 4
Aus dieser Erweiterung ergibt sich bei dem gleichen Beispiel folgendes Baumdiagramm, wenn man davon ausgeht, dass es von links nach rechts erstellt wird. Man kann leicht feststellen, dass das erste Baumdiagramm wesentlich umfangreicher ist, als das zweite. Man kann die verkürzte Laufzeit dieses erweiterten Algorithmus demnach schon an diesem Baumdiagramm ablesen. Hieraus ergeben sich bezüglich der Laufzeit des Algorithmus zwei extreme Fälle: Best case: o Der Algorithmus findet die kürzeste Lösung gleich auf dem ersten Ast (des Baumdiagramms) und kann die restlichen Lösungen zügig abbrechen, was Zeit einspart. o => Der Graph (x = Anzahl der Knotenpunkte und y = Laufzeit des Algorithmus) steigt somit nahezu linear. Worst case: o Der Algorithmus findet die Lösung erst auf dem letzten Ast (des Baumdiagramms) und muss somit erst alle anderen Möglichkeiten durchspielen, was viel Zeit bedarf. o => Der Graph (x = Anzahl der Knotenpunkte und y = Laufzeit des Algorithmus) steigt somit exponentiell an. Da jeder Aufruf des Algorithmus, je nach Anordnung der Knotenpunkte, zwischen dem best und worst case liegt, lässt sich die Laufzeit des Algorithmus in Bezug auf die Anzahl der Knotenpunkte graphisch (als Bereich) darstellen. 5
Die hellgrüne Fläche stellt die mögliche Laufzeit des Algorithmus dar. Wie man leicht erkennen kann distanzieren sich die beiden Graphen bei einer verhältnismäßig geringen Erhöhung der Anzahl der Knotenpunkte rapide. Somit vergrößert sich der Bereicht der möglichen Laufzeit. Die durchschnittliche Laufzeit befindet sich genau zwischen dem Grafen des best case (rot) und worst case (blau). 4.2 Umsetzung in Delphi Die Umsetzung in Delphi war die größte Hürde dieses Projekts. Aus diesem Grund werde ich Probleme, die aufgetreten sind auch ansprechen und einen Lösungsvorschlag bringen. Zunächst habe ich mir überlegt wie das Programm ungefähr ablaufen soll und welche bzw. wie viele Variablen der Algorithmus benötigt. Es folgt eine Beschreibung des Algorithmus des Programms. Zunächst ein Auszug des Delphi-Quelltextes des Algorithmus: 6
Zunächst habe ich für meinen Algorithmus einen Backtracking-Algorithmus in Betracht gezogen. Da bei dem Zurückspringen eine Reihe von Fehlern auftraten habe ich mich von dem Backtracking wieder etwas distanziert und den oben stehenden Algorithmus geschrieben. Das Programm funktioniert, auch wenn das Prinzip des Backtracking nicht den hauptsächlichen Weg darstellt, fehlerfrei. Mein Algorithmus wird, wie man oben sehen kann, iterativ (durch eine Schleife) aufgerufen. In der While-Schleife (6. Zeile) wird geprüft, ob man sich schon am Zielort befindet und ob der Weg bis jetzt schon länger ist, als der kürzeste zum Ziel führende Weg. In beiden Fällen würde der Vorgang abgebrochen und neu gestartet werden. Dies ist mit einem Ast des oben dargestellten Baumdiagramms, der schon zum Ziel geführt hat, oder schon zu lang ist, zu vergleichen. Auch bei dem Diagramm hat man schon gesehen, dass in diesem Fall zurückgesprungen und ein neuer Ast gewählt wird. Innerhalb dieser Schleife wird der Zufallsgenerator gestartet, um in der folgenden Zeile an einem Knotenpunkt einen zufälligen Weg nehmen zu können. In Zeile 10 wird die durch Zufall gewählte Nachbarstadt herausgefunden. In der folgenden Zeile wird der Gesamtstrecke der aktuellen Route die neu gegangene Teilstrecke addiert. In Zeile 11 wird der Variablen aktuell, in der ständig die aktuelle Stadt gespeichert ist, der neue Aufenthaltsort mitgeteilt. 7
In der folgenden Zeile wird die Variable punkt, in der die Anzahl der Knotenpunkte gespeichert ist um den neu ereichten Punkt erhöht. Anschließend werden die Variablen punkt und aktuell zurückgesetzt und die Variable rute, in der die Anzahl der Routen gespeichert ist um eins erhöht, um einen neuen Weg zu berechnen. Grundsätzlich ist zu sagen, dass innerhalb der While-Schleife eine mögliche Route berechnet wird. Genauer gesagt wird der, durch die For-Schleife neu angefangene Weg, bei jedem durchlauf dieser While-Schleife um einen Knotenpunkt erweitert. Dies ist die grundlegende Funktionsweise meines Algorithmus. In meinem Programm ist eine Anwendung dieses Algorithmus mit einigen Erweiterungen, die man sich innerhalb dieses Programms ansehen kann, dargestellt. 8