33This exercise implements the 2-opt heuristic for the Traveling Salesman Problem
44using several move-selection strategies, in both Python and C++.
55
6+ ## Updates
7+
8+ Post-release fixes. Only changes that affect your workflow are listed here;
9+ see ` git log ` for the full history.
10+
11+ - ** 2026-04-16** — Python variants (` first_improvement_two_opt ` ,
12+ ` full_scan_two_opt ` , ` best_improvement_two_opt ` ) now take an optional
13+ ` timeout: float = 10.0 ` parameter, matching the C++ API. If you already
14+ started: add the parameter to your signature and check a deadline
15+ (` time.perf_counter() ` ) in your outer loop — coarse-grained checks after
16+ each restart or full sweep are enough. Without this, ` benchmark.py ` could
17+ run for hours on large ` n ` . See issue #1 .
18+
619## Project Structure
720
821```
@@ -84,16 +97,17 @@ and prints a summary table comparing tour length and runtime.
8497
8598## Function Signatures
8699
87- All single-run functions take a list of ` (x, y) ` tuples and an ` initial_tour `
88- (a permutation of ` 0..n-1 ` ), and return the improved tour. The benchmark
89- generates the initial tour in Python and passes it to every variant, so all
90- start from the same permutation and results are directly comparable.
100+ All single-run functions take a list of ` (x, y) ` tuples, an ` initial_tour `
101+ (a permutation of ` 0..n-1 ` ), and an optional ` timeout ` in seconds, and
102+ return the improved tour. The benchmark generates the initial tour in
103+ Python and passes it to every variant, so all start from the same
104+ permutation and results are directly comparable.
91105
92106``` python
93- # Part a: Python variants
94- first_improvement_two_opt(points, initial_tour) -> list[int ]
95- full_scan_two_opt(points, initial_tour) -> list[int ]
96- best_improvement_two_opt(points, initial_tour) -> list[int ]
107+ # Part a: Python variants (all take an optional timeout in seconds)
108+ first_improvement_two_opt(points, initial_tour, timeout = 10.0 ) -> list[int ]
109+ full_scan_two_opt(points, initial_tour, timeout = 10.0 ) -> list[int ]
110+ best_improvement_two_opt(points, initial_tour, timeout = 10.0 ) -> list[int ]
97111
98112# Part b: C++ variants (all take an optional timeout in seconds)
99113cpp_first_improvement(points, initial_tour, timeout = 10.0 ) -> list[int ]
@@ -104,6 +118,11 @@ cpp_best_improvement(points, initial_tour, timeout=10.0) -> list[int]
104118parallel_two_opt(points, num_threads = 4 , base_seed = 0 , timeout = 10.0 ) -> list[int ]
105119```
106120
121+ When the timeout is reached, return the best tour found so far. Coarse-grained
122+ checks are fine: compare ` time.perf_counter() ` (Python) or ` Clock::now() ` (C++)
123+ against a precomputed deadline after each restart or full sweep, not inside
124+ the innermost loop.
125+
107126## Build Troubleshooting
108127
109128** C++ changes not taking effect?**
0 commit comments