Algorithm for random distribution of circles in a square
This simple script to place a number of num_circles circles with radius radiusin a refined rectangle (aligned to global axes) speficied by xmin, xmax, ymin and ymax by trying random locations until it not colliding with an existing circle. This is certainly not the most efficient way of doing it, but it should be fine for smaller problems and if the relative area covered is not too high.  




At some point the script will not find a free spot anymore and throw an error. If you increase max_tries, the algorithm is more likely to find free spots at the cost of additional computation time, but the needed time raises extremely fast if you need a higher covered area. It is hard to get much higher than 50%.



from mathutils import Vector
from mathutils.noise import random, seed_set
import bpy

# Specify parameters
seed = 0
xmin = -1
xmax = 1
ymin = -1
ymax = 1
radius = 0.1
num_circles = 68
max_tries = 10000

# Init
seed_set(seed)
sx = xmax-xmin-2*radius
sy = ymax-ymin-2*radius
xminm = xmin+radius 
yminm = ymin+radius 
existing_locations = []
sce = bpy.context.scene
bpy.ops.mesh.primitive_circle_add(location=(0,0,0), radius=radius)
ref_circle = bpy.context.object

# Loop
for i in range(num_circles):
    j = 0
    searchOn = True
    while searchOn:
        if j &gt; max_tries:
            bpy.ops.object.select_all(action='DESELECT')
            ref_circle.select = True
            bpy.ops.object.delete() 
            raise ValueError('Found no more room for another circle')
            break
        j += 1
        new_location = (xminm + random()*sx,
                        yminm + random()*sy,
                        0)
        for existing_location in existing_locations:
            if (Vector(existing_location)-Vector(new_location)).length &lt; 2*radius:
                break
        else:
            new_circle = ref_circle.copy()
            new_circle.location = new_location
            sce.objects.link(new_circle)
            existing_locations.append(new_location)
            searchOn = False

ref_circle.select = True
bpy.ops.object.delete() 
sce.update()