How close it gets
These three problems are standard benchmarks with mathematically proven shortest answers, so the algorithm's output can be scored against a known truth rather than against itself.
| Problem | Cities | Best found | Proven optimum | Gap |
|---|---|---|---|---|
| swiss42 | 42 | 1297 | 1273 | 1.9% |
| bayg29 | 29 | 1622 | 1610 | 0.8% |
| brazil58 | 58 | 25507 | 25395 | 0.4% |
Best of 12 seeded runs per crossover, population 100, 2000 generations, tournament selection with elitism. swiss42 measured on the corrected distance table — see The bug.
Getting within a couple of percent of a proven optimum, on a problem where checking every answer is physically impossible, is the point. The algorithm never knows how close it is.
What the sweep found
Two sweeps. The first ran every selection × crossover × mutation combination 30 times over, at a population of 50 and 750 generations. The second widened it to 576 configurations per problem, varying rates, elite sizes and fitness sharing.
- Tournament selection, PMX crossover and inversion mutation was the strongest combination, with or without elitism — closely followed by tournament, order 1 and inversion.
- Fitness proportionate selection performed badly. Once the population converges, every route is close to every other in length, so weighting by length makes selection nearly uniform and the pressure disappears. Tournament and ranking keep working precisely because they only care about order, not magnitude.
- Inversion beat the other two mutations everywhere. It is the only one whose effect on a route is geometric rather than arbitrary: reversing a run removes a single crossing.
- Elitism converged faster and made the best-so-far curve monotonic, at the cost of diversity late in the run.
The result that matters
Hitting a good route once is not the same as hitting it reliably. Out of 30 runs, how often each combination reached a route below 1400 on swiss42:
| Combination | No elitism | Elitism |
|---|---|---|
| tournament · cycle · inversion | 21 | 21 |
| tournament · order 1 · inversion | 15* | 16 |
| tournament · PMX · inversion | 12 | 11 |
| ranking · order 1 · inversion | 1 | 0 |
* recorded originally as a rate of 1/2 rather than a count out of 30; shown here as the equivalent 15.
PMX won the headline on the strength of its single best result. But cycle crossover was almost twice as likely to land a good route on any given run — 21 times out of 30 against 12. If you get one attempt, you want cycle. If you can afford thirty and keep the best, you want PMX.
That distinction is easy to miss when you report only the best number, and it is the kind of thing that decides whether a method survives contact with production.
Re-running it today
The original Python still runs. Five runs per combination on swiss42, population 50, 750 generations — a small sample, and the spread is wide enough that only the last row is clearly separated:
| Combination | Best | Mean |
|---|---|---|
| tournament · PMX · inversion | 1307 | 1367 |
| tournament · order 1 · inversion | 1308 | 1404 |
| tournament · cycle · inversion | 1367 | 1462 |
| ranking · order 1 · inversion | 1438 | 1539 |
Proving the browser version is faithful
The demo is a JavaScript port of the same operators, not a reimplementation from a textbook. A port is only useful if it behaves identically, so I tested it rather than assuming: 400 random pairs of parent routes, cut points pinned to match, both versions run side by side.
Cycle, PMX, order 1, swap and inversion all returned byte-identical output, 400 times out of 400. Shuffle is excluded because it draws randomness beyond its cut points.
The port departs from the Python in two deliberate places. Randomness is seeded, so a run can be repeated exactly. And children are scored against the problem you selected — the Python builds them without passing a distance table, so it silently falls back to its swiss42 default whenever you ask it for bayg29 or brazil58.