Heilbronn's triangle problem
Better configurations for Heilbronn’s triangle problem in the unit square, n = 11–14
Merge pull request #5 from annealing
Browse filesAnnealing search: a reproducible n = 11 configuration
Checks
verify
Details
+20 −0 routes/annealing/README.md added View file
| @@ -0,0 +1,20 @@ | ||
| 1 | +--- | |
| 2 | +issue: 2 | |
| 3 | +status: working | |
| 4 | +--- | |
| 5 | +# Route: annealing | |
| 6 | + | |
| 7 | +## What happened | |
| 8 | + | |
| 9 | +- n = 11: min area 0.01399 after 40 restarts (n11.txt). | |
| 10 | +- Restarts matter more than the cooling schedule. | |
| 11 | + | |
| 12 | +## Reproduce | |
| 13 | + | |
| 14 | +``` | |
| 15 | +python3 verify.py routes/annealing/*.txt | |
| 16 | +``` | |
| 17 | + | |
| 18 | +## Next | |
| 19 | + | |
| 20 | +Try n = 12 and 13; try symmetric starts (#3). | |
+11 −0 routes/annealing/n11.txt added View file
| @@ -0,0 +1,11 @@ | ||
| 1 | +0.863163 0.039121 | |
| 2 | +0.306908 0.001735 | |
| 3 | +0.164578 0.993940 | |
| 4 | +0.000000 0.715573 | |
| 5 | +0.141183 0.042499 | |
| 6 | +0.291364 0.780188 | |
| 7 | +0.591522 0.520192 | |
| 8 | +0.966230 0.458159 | |
| 9 | +0.119212 0.345431 | |
| 10 | +0.998641 0.273022 | |
| 11 | +0.333991 0.487438 | |
+39 −0 routes/annealing/search.py added View file
| @@ -0,0 +1,39 @@ | ||
| 1 | +"""Simulated annealing for Heilbronn's problem: maximise the smallest triangle area.""" | |
| 2 | +import itertools | |
| 3 | +import math | |
| 4 | +import random | |
| 5 | +import sys | |
| 6 | + | |
| 7 | + | |
| 8 | +def min_area(p): | |
| 9 | + return min(abs((b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1])) / 2 for a, b, c in itertools.combinations(p, 3)) | |
| 10 | + | |
| 11 | + | |
| 12 | +def anneal(n, steps=20000, seed=0): | |
| 13 | + rng = random.Random(seed) | |
| 14 | + p = [(rng.random(), rng.random()) for _ in range(n)] | |
| 15 | + cur = best = min_area(p) | |
| 16 | + best_p = p[:] | |
| 17 | + for k in range(steps): | |
| 18 | + t = 0.01 * (1 - k / steps) + 1e-6 | |
| 19 | + i = rng.randrange(n) | |
| 20 | + old = p[i] | |
| 21 | + p[i] = tuple(min(1, max(0, c + rng.gauss(0, t))) for c in old) | |
| 22 | + new = min_area(p) | |
| 23 | + if new >= cur or rng.random() < math.exp((new - cur) / t): | |
| 24 | + cur = new | |
| 25 | + if new > best: | |
| 26 | + best, best_p = new, p[:] | |
| 27 | + else: | |
| 28 | + p[i] = old | |
| 29 | + return best, best_p | |
| 30 | + | |
| 31 | + | |
| 32 | +if __name__ == "__main__": | |
| 33 | + n = int(sys.argv[1]) | |
| 34 | + restarts = int(sys.argv[2]) if len(sys.argv) > 2 else 40 | |
| 35 | + steps = int(sys.argv[3]) if len(sys.argv) > 3 else 20000 | |
| 36 | + best = max((anneal(n, steps, seed=s) for s in range(restarts)), key=lambda r: r[0]) | |
| 37 | + print(f"# n={n} min area {best[0]:.6f}") | |
| 38 | + for x, y in best[1]: | |
| 39 | + print(f"{x:.6f} {y:.6f}") | |