Traveling Salesman Problem

Das Traveling Salesman Problem (TSP) ist ein Klassiker der kombinatorischen Optimierung. Gegeben sind n Punkte (Städte). Gesucht ist die kürzeste geschlossene Rundreise, die jeden Punkt genau einmal besucht. Der Suchraum wächst exponentiell, weshalb in der Praxis nahezu ausschließlich heuristische Verfahren eingesetzt werden. Solche Verfahren liefern nicht zwangsläufig die optimale Rundreise, erreichen aber in kurzer Zeit sehr gute Lösungen. Im hier betrachteten Beispiel (TSPLIB pcb442) liegt die beste gefundene Tourlänge bei einem Abstand von etwa 0,13 % zum bekannten Optimum.

Im Mittelpunkt dieses Beitrags steht ein kompaktes, reproduzierbares CLI-Tool in C++20, mit dem sich Heuristiken und Parameter systematisch testen lassen. Neben der Tourlänge werden auch der Suchverlauf und Zwischenstände ausgegeben, sodass der Optimierungsprozess über eine SVG/WebP-Visualisierung nachvollziehbar wird.

Testproblem: pcb442 (TSPLIB)

Punktliste pcb442
Abbildung: Punktliste der TSPLIB-Instanz pcb442.

Als Benchmark dient die TSPLIB-Instanz pcb442 mit 442 Punkten in der Ebene. Schon für dieses relativ kleine Beispiel gibt es unfassbar viele Möglichkeiten: 441!/2 (ungefähr 1.2e976, also eine Zahl mit 977 Stellen). Die Größe dieser Zahl sprengt jede Vorstellung; selbst der Durchmesser des Universums in Millimetern entspricht nur etwa 29!, also extrem weit entfernt von 441!. Für diese Instanz ist ein Referenzwert (Best Known / Optimum) der Tourenlänge von 50778 bekannt.

Distanzdefinition (EUC_2D)

In der TSPLIB ist für pcb442 der Kantengewichtstyp EUC_2D definiert. Die euklidische Distanz zwischen zwei Punkten wird dabei auf die nächste ganze Zahl gerundet. Reinelt gibt dafür folgende Referenzimplementierung an:

xd = x[i] - x[j];
yd = y[i] - y[j];
dij = nint( sqrt( xd*xd + yd*yd ) );

Durch diese Rundung pro Kante sind TSPLIB-Tourlängen bei EUC_2D ganzzahlig. Verwendet man stattdessen die kontinuierliche euklidische Distanz ohne Rundung, entstehen nicht-ganzzahlige Tourlängen, die nicht direkt mit TSPLIB-Referenzwerten vergleichbar sind.

Algorithmischer Ansatz

Der Solver arbeitet mit lokalen Verbesserungszügen aus der k-opt-Familie. Im Kern werden Kanten der aktuellen Tour entfernt und die dadurch entstehenden Teilstücke neu verbunden; die resultierende Längenänderung Δ lässt sich effizient lokal berechnen.

Als Moves werden 2-opt und 3-opt verwendet. Beide Moves operieren ausschließlich auf der aktuellen Tour und erfordern keine zusätzlichen Hilfsstrukturen. 2-opt invertiert ein Segment der Tour (zwei Kanten werden ersetzt), während 3-opt drei Kanten ersetzt und damit Touren verbessern kann, die bereits 2-opt-optimal sind. Zusätzlich existiert ein Mix-Modus (z. B. 4× 2-opt + 1× 3-opt), der schnelle Strukturverbesserungen mit gezieltem Feintuning kombiniert.

Die eigentliche Steuerung der Suche erfolgt über unterschiedliche Akzeptanzregeln. Diese bestimmen, ob ein Schritt übernommen wird:

  • Simulated Annealing (SA): Verschlechterungen werden temperaturabhängig probabilistisch akzeptiert.
  • Threshold Accepting: Akzeptiert Schritte, solange Δ unter einer dynamischen Schwelle liegt.
  • Flooding: Akzeptiert Schritte bis zu einem aktuellen „Flood-Level“, das schrittweise abgesenkt wird.

Move-Typ (2-opt/3-opt/Mix) und Akzeptanzregel sind unabhängig kombinierbar. So lässt sich das Explorationsverhalten steuern, ohne die Nachbarschaft zu ändern.

Ergebnisse

Die Suche ist stochastisch, da Kandidaten zufällig ausgewählt werden. Bei festem Zufallsseed und identischen Parametern ist der gesamte Optimierungsverlauf jedoch vollständig reproduzierbar. Die verwendete Konfiguration wird zusammen mit der besten Tour gespeichert.

Für pcb442 wurden mit 3-opt Tourlängen um 50842 erreicht, was einem Gap von ca. 0.126 % zum bekannten Optimum 50778 entspricht. Das ist für eine bewusst schlanke Implementierung mit lokaler Suche ein sehr gutes Ergebnis.

Zusätzlich werden die besten Läufe in einem kleinen Leaderboard (Top-N) gesammelt, inklusive tour.txt und der zugehörigen config.json. Damit lassen sich erfolgreiche Parameterkombinationen später exakt reproduzieren.

Auswertung der Testläufe

Bestes Ergebnis: 50842 (Best Known: 50778, Gap: 64 bzw. 0,126 %). Laufzeit des besten Laufs: ca. 195 s.

In den Sweeps dominiert Threshold + 3-opt. Flooding ist deutlich schneller, bleibt aber in der Ergebnisqualität hinter Threshold zurück; Simulated Annealing ist in dieser Konfiguration klar schwächer.

Auszug aus dem Leaderboard

Rang  Länge  Gap%   Algorithmus   Move   Seed  Laufzeit
1     50842  0.126  threshold     3-opt  7777  ~197s
2     51075  0.585  threshold     3-opt  -     ~201s
3     51128  0.689  threshold     3-opt  -     ~110s
4     51133  0.699  threshold     3-opt  -     ~302s
5     51158  0.748  threshold     3-opt  -     ~208s
6     51212  0.855  threshold     3-opt  -     ~283s
7     51236  0.902  flooding      3-opt  -     ~44s
8     51410  1.245  threshold     3-opt  4242  ~198s
9     51431  1.286  threshold     3-opt  -     ~97s
10    51448  1.319  threshold     3-opt  -     ~182s
11    51455  1.333  threshold     3-opt  -     ~98s
12    51484  1.390  flooding      3-opt  -     ~22s
13    51534  1.489  flooding      3-opt  -     ~56s
14    51536  1.493  threshold     3-opt  -     ~270s
15    51581  1.581  threshold     3-opt  -     ~104s
16    51672  1.761  flooding      3-opt  -     ~29s
17    51682  1.780  threshold     2-opt  -     ~46s
18    51699  1.814  flooding      3-opt  -     ~112s
19    51754  1.922  flooding      3-opt  -     ~65s
20    51795  2.003  threshold     mix    -     ~75s
23    54465  7.261  sa            3-opt  -     ~119s 

Seed = “-” bedeutet: Standard-Seed aus der config.json (hier 2121).

Alle Läufe sind reproduzierbar, da jede Top-Run im Leaderboard mit tour.txt, config.json und stats.txt gespeichert wird.

Steuerung per JSON

Die Parameter werden über eine config.json gesteuert. Das erleichtert reproduzierbare Experimente, weil jede Änderung der Suche dokumentiert ist. Ein Auszug aus der Datei:

{
  "algorithm": "threshold",
  "input": "data/tsplib/PCB442.TSP",
  "distance_mode": "euc_2d",
  "move_mode": "3opt",
  "moves_per_city": 800,
  "max_outer_iterations": 400
}

Visualisierung

Der Solver erzeugt SVGs der Tour sowie optional Zwischenstände pro Iteration. Aus den Frames lässt sich ein kurzer Film (z. B. WebP) erzeugen, der die schrittweise Strukturverbesserung sichtbar macht. Der Film endet automatisch, sobald keine weiteren akzeptierten Verbesserungen gefunden werden.

Aktuelle Tour (SVG):

TSP Tour pcb442
Abbildung: Beste gefundene Tour (EUC_2D).

Optimierungsprozess (WebP):

Optimierungsfilm pcb442
Abbildung: Animationssequenz des Optimierungsverlaufs (verkürzt).

Ausblick

Naheliegende Erweiterungen sind Candidate-Edges (z.B. k-nearest neighbors), gezieltere 3-opt-Varianten sowie parallele Moves (OpenMP/TBB). Damit ließe sich die Qualität weiter steigern, ohne den Charakter des Solvers zu verändern.

Die verwendeten Ideen (lokale Suche + Akzeptanzregeln) lassen sich auch außerhalb des klassischen TSP einsetzen: z. B. bei Routenplanung (Lieferungen, Service-Touren), Fertigungs- und Bohrpfaden (z. B. PCB- oder CNC-Reihenfolgen), Kommissionierung im Lager, Job-Scheduling und allgemeinen Sequenzierungsproblemen. Über angepasste Distanz-/Kostenfunktionen kann das Verfahren auf viele Optimierungsaufgaben mit Tour- oder Reihenfolgestruktur übertragen werden.

Anmerkung: Der Code geht auf einen älteren Ansatz zurück, wurde jedoch vollständig modernisiert.

Referenzen:
TSPLIB 95 (Reinelt),
JEI – Ant Colony Optimization (pcb442: 50778),
TSPLIB (GitHub Mirror).