Given n circles with radii r1 ... rn, position them in such a way that no circles are overlapping and the bounding circle is of \"small\" radius.
The program takes a lis
Can you treat the circles as charged particles in a charged cavity and look for a stable solution? That is, circles repel one another according to proximity, but are attracted towards the origin. A few steps of simulation might get you a decent answer.
http://en.wikipedia.org/wiki/Apollonian_gasket
This seems somewhat relevant to what you are trying to do, and may provide some potential constraints for you.
Sounds like a Circle Packing problem, here is some information:
You could try using a 2d physics library, and just pour your 2d circles into a larger circular container - and wait for them to settle into place.
I have a pretty naive one pass (over the radii) solution that produces alright results, although there is definitely room for improvement. I do have some ideas in that direction but figure I might as well share what I have in case anybody else wants to hack on it too.
It looks like they intersect at the center, but they don't. I decorated the placement function with a nested loop that checks every circle against every other circle (twice) and raises an AssertionError
if there is an intersection.
Also, I can get the edge close to perfect by simply reverse sorting the list but I don't think the center looks good that way. It's (pretty much the only thing ;) discussed in the comments to the code.
The idea is to only look at discrete points that a circle might live at and iterate over them using the following generator:
def base_points(radial_res, angular_res):
circle_angle = 2 * math.pi
r = 0
while 1:
theta = 0
while theta <= circle_angle:
yield (r * math.cos(theta), r * math.sin(theta))
r_ = math.sqrt(r) if r > 1 else 1
theta += angular_res/r_
r += radial_res
This just starts at the origin and traces out points along concentric circles around it. We process the radii by sorting them according to some parameters to keep the large circles near the center (beginning of list) but enough small ones near the beginning to fill in spaces. We then iterate over the radii. within the main loop, we first loop over points that we have already looked at and saved away. If none of those are suitable, we start pulling new points out of the generator and saving them (in order) until we find a suitable spot. We then place the circle and go through our list of saved points pulling out all of the ones that fall within the new circle. We then repeat. on the next radius.
I'll put some ideas I have into play and make it mo`bettah. This might serve as a good first step for a physics based idea because you get to start with no overlaps. Of course it might already be tight enough so that you wouldn't have much room.
Also, I've never played with numpy or matplotlib so I write just vanilla python. There might be something in there that will make it run much faster, I'll have to look.
Not a solution, just a brainstorming idea: IIRC one common way to get approximate solutions to the TSP is to start with a random configuration, and then applying local operations (e.g. "swapping" two edges in the path) to try and get shorter and shorter paths. (Wikipedia link)
I think something similar would be possible here:
The interesting question is: what kind of "iterative improvement operator" could you use in step 3? We can assume that the positions at that stage are locally optimal, but they might be improved by rearranging a large fraction of the circles. My suggestion would be to arbitrarily choose a line through the circles. Then take all the circles "left" of the line and mirror them at some axis perpendicular to that line: You would probably try multiple lines and pick the one that leads to the most compact solution.
The idea is, if some of the circles are already at or close to their optimal configuration, chances are good this operation won't disturb them.
Other possible operations I could think of:
Then you could pick one of the circles adjacent to the largest between-circle-area (the red area, in the image) and swap it with another circle, or move it somewhere to the boundary.
(Response to comment:) Note that each of these "improvements" is almost guaranteed to create overlaps and/or unneccessary space between circles. But in the next iteration, step 2 will move the circles so they are tightly packed and non-overlapping again. This way, I can have one step for local optimizations (without caring about global ones), and one for global optimizations (which might create locally suboptimal solutions). This is far easier than having one complex step that does both.