The Traveling Salesman Problem: From 19th Century Puzzles to Genetic Algorithms
A salesman must visit a set of cities exactly once and return home, minimizing total travel distance. This deceptively simple puzzle—the Traveling Salesman Problem (TSP)—has haunted mathematicians, computer scientists, and logistics planners for nearly two centuries. It remains one of the most studied problems in computational optimization. Historical Origins The TSP's roots trace to the 1830s, when Irish mathematician William Rowan Hamilton and British mathematician Thomas Kirkman studied related mathematical problems involving traversing graph vertices. Hamilton created the "Icosian Game" in 1857, a puzzle requiring players to find paths visiting each vertex of a dodecahedron exactly once. The problem as we know it crystallized in the 1930s. Karl Menger studied the "messenger problem" in Vienna, and Hassler Whitney at Princeton coined the name "Traveling Salesman Problem." The 1940s brought the first serious computational attacks when mathematicians at RAND Corporation, including George Dantzig and Delbert Ray Fulkerson, began developing solution methods. In 1954, Dantzig, Fulkerson, and Selmer Johnson solved a 49-city instance optimally—a remarkable achievement for the era. Their cutting-plane method laid groundwork for integer programming. By the 1970s, the TSP had become the canonical example of NP-hard problems, meaning no known algorithm can solve all instances efficiently. Why It Matters The TSP's significance extends far beyond academic curiosity: Logistics and Transportation: Delivery companies, from postal services to Amazon, solve TSP variants daily. FedEx and UPS route thousands of vehicles through millions of potential paths. Even small improvements translate to massive fuel savings. Manufacturing: Circuit board drilling, where a machine must visit thousands of holes in optimal order, is a direct TSP application. Chip fabrication uses similar routing for wire placement. DNA Sequencing: Genome assembly involves finding optimal orderings of DNA fragments—structurally identical to TSP. Telescope Scheduling: Astronomical surveys must observe targets across the sky efficiently, minimizing slew time between observations. The TSP belongs to the NP-hard complexity class. For n cities, there are (n-1)!/2 possible routes. Ten cities yield 181,440 routes—tractable by brute force. Twenty cities produce over 60 quadrillion routes. Fifty cities exceed the number of atoms in the observable universe. This exponential explosion means exact solutions become impractical beyond a few dozen cities. Real-world applications with thousands of locations require approximation methods. Genetic Algorithms: Evolution as Optimizer Genetic algorithms (GAs) apply biological evolution principles to optimization. A population of candidate solutions evolves through selection, crossover, and mutation, gradually improving over generations. For TSP: Individual: A sequence of cities representing a tour Fitness: Total tour distance (lower is better) Selection: Better solutions reproduce more frequently Crossover: Combine routes from two parent solutions Mutation: Randomly swap cities within a route GAs don't guarantee optimal solutions but often find excellent approximations quickly—making them practical for large instances where exact methods fail. Solving TSP with Python and DEAP DEAP (Distributed Evolutionary Algorithms in Python) provides a flexible framework for implementing genetic algorithms. The library includes an official TSP example that demonstrates the core concepts. Here's an adapted version solving a 20-city TSP: import random import numpy as np from deap import algorithms, base, creator, tools # Generate 20 random cities as (x, y) coordinates NUM_CITIES = 20 random.seed(42) CITIES = [(random.uniform(0, 100), random.uniform(0, 100)) for _ in range(NUM_CITIES)] def distance(city1, city2): """Euclidean distance between two cities.""" return np.sqrt((city1[0] - city2[0])**2 + (city1[1] - city2[1])**2) # Precompute distance matrix DISTANCE_MATRIX = [[distance(CITIES[i], CITIES[j]) for j in range(NUM_CITIES)] for i in range(NUM_CITIES)] def evaluate(individual): """Calculate total tour distance.""" total = DISTANCE_MATRIX[individual[-1]][individual[0]] # Return to start for i in range(len(individual) - 1): total += DISTANCE_MATRIX[individual[i]][individual[i + 1]] return (total,) # Must return tuple # DEAP setup creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) # Minimize creator.create("Individual", list, fitness=creator.FitnessMin) toolbox = base.Toolbox() # Individual: random permutation of city indices toolbox.register("indices", random.sample, range(NUM_CITIES), NUM_CITIES) toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.indices) toolbox.register("population", tools.initRepeat, list, toolbox.individual) # Genetic operators toolbox.register("evaluate", evaluate) toolbox.register("mate", tools.cxOrdered) # Order crossover preserves city visits toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.05) toolbox.register("select", tools.selTournament, tournsize=3) def main(): random.seed(42) # Create population pop = toolbox.population(n=300) # Track best solution hof = tools.HallOfFame(1) # Statistics stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("min", np.min) stats.register("avg", np.mean) # Run evolution pop, log = algorithms.eaSimple( pop, toolbox, cxpb=0.7, # Crossover probability mutpb=0.2, # Mutation probability ngen=100, # Generations stats=stats, halloffame=hof, verbose=True ) best = hof[0] print(f"\nBest tour distance: {evaluate(best)[0]:.2f}") print(f"Best route: {best}") return pop, log, hof if __name__ == "__main__": main() Key Implementation Details Order Crossover (cxOrdered): Standard crossover would produce invalid tours with duplicate or missing cities. Order crossover preserves visit sequences from parents while maintaining valid permutations. Shuffle Mutation (mutShuffleIndexes): Swaps cities within a tour with probability 0.05 per position. This local modification explores neighboring solutions. Tournament Selection: Randomly samples three individuals and picks the fittest. Balances selection pressure with diversity. Running this code typically converges to within 5-10% of optimal for 20 cities within seconds. Larger instances may require parameter tuning or advanced techniques like island models or local search hybridization. Beyond Basic GAs Modern TSP solvers combine multiple techniques: Lin-Kernighan heuristic: Local search that swaps edge sequences Ant Colony Optimization: Probabilistic technique inspired by ant pheromone trails Simulated Annealing: Accepts worse solutions with decreasing probability Branch and Bound: Exact method pruning infeasible regions The current record for proven optimal TSP solution stands at 85,900 cities, achieved in 2006 after years of computation. Practical applications typically use heuristics that find solutions within 1-2% of optimal. Conclusion The Traveling Salesman Problem bridges pure mathematics and practical engineering. Its simple statement belies profound computational complexity. While we can't efficiently solve it exactly for large instances, approximation methods like genetic algorithms provide useful solutions for real-world applications. The TSP reminds us that some problems resist elegant solutions. Progress comes not from breakthroughs but from accumulating clever techniques—each shaving percentages off tour lengths, each saving fuel, time, and resources at scale. For logistics companies routing millions of packages, those percentages translate to billions of dollars. The salesman's ancient puzzle remains urgently relevant.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to