Autor: Nils Becker

Das Traveling-Salesman-Problem – oder „Problem des Handelsreisenden“ – ist ein in der Informatik bekanntes Optimierungsproblem. Bildlich gesprochen: Der „Handelsreisende“ hat eine Palette von Produkten, die er verkaufen möchte. Deswegen reist er von Stadt zu Stadt und bietet seine Waren an. Um Geld zu sparen, möchte er die Strecke, die er zurücklegen muss, minimieren. Je nachdem in welcher Reihenfolge er die Städte besucht können hohe Fahrtkosten entstehen.

Das Problem scheint trivial zu sein, ist es aber nicht. Bei drei Städten sind die möglichen Reihenfolgen noch sehr eingeschränkt. Es gibt sechs verschiedene Möglichkeiten die Zielorte nacheinander zu besuchen. Bei zehn Städten sind es schon mehr als 3,6 Millionen Kombinationen! Alle Möglichkeiten zu prüfen, funktioniert nicht mehr. Daher werden anderweitige Lösungsmethoden benötigt.

Biologie als Vorbild: Evolutionärer Algorithmus

Hierzu kann ein evolutionärer Algorithmus eingesetzt werden. Evolutionäre Algorithmen sind ein von der Biologie inspiriertes Optimierungsverfahren. Dabei gibt es eine Anfangspopulation, aus der in jeder Iteration „Nachkommen“ generiert werden. Diese werden anschließend anhand einer Fitnessfunktion bewertet und selektiert, ganz nach dem Motto „Survival of the fittest“. Aus den besten „Individuen“ wird eine neue Generation erzeugt. Dabei werden die Genome der „Elternteile“ miteinander kombiniert. Die neue Generation unterläuft anschließend einen Mutationsprozess, wodurch ein Teil des Genoms zufällig geändert wird.

Mutationsprozess

So viel zur Theorie, doch wie sieht das in der Praxis aus? Um das Problem des Handelsreisenden zu lösen, müssen erstmal die Städte ausgewählt werden, die besucht werden sollen. Hier bieten sich die Hauptstädte Deutschlands an. Neben den Städtenamen wird deren geographische Lage benötigt. Der Datensatz sieht wie folgt aus:

stadt,breitengrad,laengengrad
Berlin,52.5170365,13.3888599
Düsseldorf,51.2254018,6.7763137
Kiel,54.3227085,10.135555
Potsdam,52.4009309,13.0591397
Magdeburg,52.1315889,11.6399609
Dresden,51.0493286,13.7381437
München,48.1371079,11.5753822
Stuttgart,48.7784485,9.1800132
Saarbrücken,49.234362,6.996379
Mainz,50.0012314,8.2762513
Wiesbaden,50.0820384,8.2416556
Erfurt,50.9777974,11.0287364
Hannover,52.3744779,9.7385532
Schwerin,53.6288297,11.4148038
Hamburg,53.550341,10.000654
Bremen,53.0758196,8.8071646

Um den Datensatz einzulesen kann die Pandas-Bibliothek verwendet werden. Nebenbei können weitere hilfreiche Bibliotheken importiert werden, wie die Geopy-Bibliothek, welche für die Berechnung der Distanz zwischen zwei geographischen Koordinaten genutzt wird.

Copy to Clipboard

Da nun das zu optimierende Problem und die Daten bekannt sind, kann eine entsprechende Codierung des Genoms implementiert werden. Weil die Reihenfolge der zu besuchenden Städte optimiert werden soll, besteht das Genom jedes Individuums aus einer Permutation der Städtenamen, welche in einer Liste gehalten werden.

Die Initialisierung der Anfangspopulation geschieht mit folgendem Code:

Copy to Clipboard

Der Code generiert 100 Permutationen auf der Menge der Städtenamen. Der nächste Schritt ist die Beurteilung der Individuen anhand einer Fitnessfunktion. Als solche bietet sich die Distanz der Reise an. Da es sich hierbei um geographische Koordinaten handelt, kann nicht die euklidische Distanz verwendet werden, da sonst die Erdkrümmung außer Acht gelassen würde. Die Geopy-Bibliothek bietet eine entsprechende Funktion zur Abstandsbestimmung an.

Die Fitnessfunktion sieht dann wie folgt aus:

Copy to Clipboard

Damit die Fitnessfunktion funktioniert, wird noch eine weitere Funktion benötigt, die die Koordinaten anhand des Städtenamens zurückgibt.

Copy to Clipboard

Der nächste Schritt führt schon in die Optimierungsschleife, daher wird ein passendes Abbruchkriterium benötigt. In diesem Fall wird die Suche abgebrochen, sobald nach 1.000 Iterationen keine Verbesserung der Fitness beim besten Individuum erzielt wurde. Die Beurteilung der Fitness der Individuen findet direkt zu Beginn der Optimierungsschleife statt.

Copy to Clipboard

Danach findet die Selektion statt. Hier gibt es viele verschiedene Möglichkeiten die Individuen auszuwählen. Eine einfache und effektive Variante ist, die besten Individuen der Population zu verwenden.

Copy to Clipboard

Damit eine höhere Diversität bei der Generierung der Nachkommen gewährleistet ist, werden die ausgewählten Individuen gemischt. Für jedes Elternpaar werden nun zwei Nachkommen mittels Rekombination generiert. Es gibt verschiedene Arten wie die Rekombination stattfinden kann. Die ausgewählte Methode hängt vom Anwendungsfall ab. In diesem Fall liegt eine Besonderheit vor, da jede Stadt nur einmal im Lösungskandidaten vorhanden sein darf. Es muss sichergestellt sein, dass bei der Rekombination keine ungültigen Individuen generiert werden. Dies wäre bei einem gewöhnlichen Crossover zum Beispiel der Fall. Daher wird das sogenannte PMX-Crossover mit zwei Crossoverpunkten verwendet.

Copy to Clipboard

Die Crossoverpunkte werden dabei zufällig gewählt. Es werden für jedes Elternpaar zwei Nachkommen generiert. Anschließend findet eine Mutation der Nachkommen statt, indem zwei Städte die Position tauschen. Die zu tauschenden Positionen werden zufällig ausgewählt. Die Eltern werden nicht verändert, sodass gewährleistet ist, dass die besten Individuen in der nächsten Generation erhalten bleiben. Die neue Generation besteht dann aus den Eltern und den generierten Nachkommen.

Copy to Clipboard

Das ist auch schon die komplette Optimierungsschleife. Zu beachten ist, dass die Lösung nur eine Approximation ist. Es ist nicht garantiert, dass die abschließende Lösung tatsächlich die beste ist. Als Letztes muss noch das beste Individuum beziehungsweise die Lösung ausgegeben und dargestellt werden. Hierfür kann die Plotly-Bibliothek verwendet werden. Dabei wird als Erstes ein Pandas-Datenframe mit den Städten entsprechend der gefundenen Reihenfolge aufgebaut. Die erste Stadt in der Lösung wird am Ende nochmal angehängt, schließlich soll es eine komplette Runde sein. Die Methode line_geo plottet anschließend die im Datenframe befindlichen Koordinaten und Linien zwischen den einzelnen Datenpunkten auf eine Karte.

Copy to Clipboard

Man kann die verschiedenen Parameter weiter anpassen und das Verfahren mehrmals laufen lassen, um bessere Ergebnisse zu erzielen. So kann man zum Beispiel die Größe der Elternpopulation verringern, dadurch wird die Berechnung beschleunigt. Allerdings kann die Qualität des Ergebnisses darunter leiden. Es empfiehlt sich verschiedene Parameterkombinationen auszuprobieren, um das optimale Ergebnis zu erreichen. Das Genom der unten abgebildeten Route sieht wie folgt aus: [‚Kiel‘, ‚Hamburg‘, ‚Bremen‘, ‚Hannover‘, ‚Düsseldorf‘, ‚Wiesbaden‘, ‚Mainz‘, ‚Saarbrücken‘, ‚Stuttgart‘, ‚München‘, ‚Erfurt‘, ‚Dresden‘, ‚Berlin‘, ‚Potsdam‘, ‚Magdeburg‘, ‚Schwerin‘]. Die Route ist 2263 Kilometer lang.

Deutschlandkarte

Abbildung 1: Eine optimierte Route mit 2263 Kilometer Länge.

Jetzt teilen auf:

Kommentar

  1. Volker Koster 3. September 2023 at 10:17 - Reply

    sehr schöner Artikel!
    Ich musste im Studium noch die Ungarische Methode lernen :-)
    VG volker koster

Jetzt kommentieren