A natural way to try to approximately solve the problem is to pretend that the points are particles that have repulsive forces between them, and then run a computer simulation of initially randomly distributed particles moving under the influence of these forces, with some frictional damping.
Inspired by this paper, I initially worked with a repulsive force inversely proportional to dp, where d is the distance between the points and p is an exponent that is ramped up as the simulation progresses.
Experimenting with various parameters, I found it was helpful to start with p=1 and go up to p=4.5 and then stay at p=4.5 for a while before finishing the simulation. Velocity-dependent friction seems to work a little better than velocity-independent friction. The physical precision of the simulation, of course, doesn't matter at all, except as a means to getting a large minimum spacing. For 500 points, with 1000 steps of Euler-Cromer simulation and carefully tuned parameters (friction, dynamic step size, ramping schedule), I was able to get a minimum spacing of about 0.153 in one run. There is a theorem by Fejes-Tóth that implies one can't do better than 0.1702, so it's pretty close.
A hint from the above-linked paper helped along the way: one can arrange the initial positions of the particles to be symmetric around the origin (i.e., if we place one particle is at x, we place another at −x). Then we only need to simulate the motions of half of the particles, since the movements of the other half are just a reflection about the origin. Of course, this optimization only works if n is even (though if n is odd, it still may be worth arranging all but one particles symmetrically initially).
Then I had another idea. At each simulation time, we already calculate the current distance dmin between the two particles closest together, and what we want to do is to particularly strongly push apart those particles whose distance is close to that. After a fair amount of fine-tuning, I ended up modifying the repulsive force to (d−cdmin)p, where now we ramp p from 1 to 4.5, and c from 0 to 0.9. The result was noticeably better: my best answer for n=500 went from 0.153 to about 0.162, and typical runs give me about 0.161 after only 500 steps.
You can can generate an OpenSCAD golfball from the code by using a -scad option instead. Unfortunately, OpenSCAD is really slow in processing the output of tammes. The golfball on the right has n=336. I seem to have read that that's a pretty normal n for golfballs.
I managed to get about a 2% improvement by adding 50 iterations of a cleanup stage. At each cleanup stage, I go through the particles, and I add a bit of greedy optimization by first shifting each particle from its nearest neighbor (unless there is a tie) and then from the midpoint of its two nearest neighbors.
ReplyDeleteI improved the greedy optimization step by taking the six nearest neighbors of each point, and then trying to move the point to the circumcenter of each of the 20 triangles formed by these neighbors (I think--haven't written down a formal proof, though--that at the optimum, each point will have to be at such a circumcenter). And then repeating this process 50 times.
ReplyDeleteOne can also specify -g on the tammes commandline in order to start with a golden angle spiral instead of a random arrangement.