The smallest circle containing all points. Unique, and determined by at most 3 of the points.

The key facts

  1. The minimum enclosing circle is unique.
  2. It is determined by 2 points (as a diameter) or 3 points (on its boundary).
  3. Therefore a brute-force solution exists — but Welzl’s algorithm does it in expected .

Welzl’s algorithm — expected

struct Circle { PD c; double r; };
 
bool inside(const Circle& ci, PD p) { return norm2(p - ci.c) <= ci.r * ci.r + EPS; }
Circle from2(PD a, PD b) { return {(a + b) * 0.5, abs_(a - b) / 2}; }
Circle from3(PD a, PD b, PD c) { PD o = circumcenter(a, b, c); return {o, abs_(o - a)}; }
 
Circle minEnclosingCircle(vector<PD> p) {
    shuffle(p.begin(), p.end(), rng);                 // ESSENTIAL
    Circle c{p[0], 0};
    for (int i = 0; i < (int)p.size(); i++) {
        if (inside(c, p[i])) continue;
        c = {p[i], 0};
        for (int j = 0; j < i; j++) {
            if (inside(c, p[j])) continue;
            c = from2(p[i], p[j]);
            for (int k = 0; k < j; k++) {
                if (inside(c, p[k])) continue;
                c = from3(p[i], p[j], p[k]);
            }
        }
    }
    return c;
}

Three nested loops that look — but the analysis says otherwise.

Why it is — backwards analysis

After processing points, the circle is determined by at most 3 of them. Since the order was random, the probability that the -th point is one of those 3 is . So the expected work at level is , and the total is .

The same argument at each nesting level keeps the whole thing linear. This is backwards analysis, the same technique that analyses Seidel’s LP and randomised incremental Delaunay.

The shuffle is not optional

Without it, an adversarial input (points already sorted so each new one is outside) makes the algorithm . Shuffle with a time-seeded RNG so the order cannot be predicted.

Circumcentre

PD circumcenter(PD a, PD b, PD c) {
    PD ba = b - a, ca = c - a;
    double d = 2 * cross(ba, ca);
    double x = (ca.y * norm2(ba) - ba.y * norm2(ca)) / d;
    double y = (ba.x * norm2(ca) - ca.x * norm2(ba)) / d;
    return a + PD{x, y};
}

Degenerate when the three points are collinear () — in that case the enclosing circle is determined by the two extreme points, which the algorithm’s structure already handles.

LP-type problems

Minimum enclosing circle is the archetypal LP-type problem: an optimisation over a set system with bounded combinatorial dimension (3 here), satisfying monotonicity and locality. The same framework solves:

ProblemCombinatorial dimension
Minimum enclosing circle3
Minimum enclosing ball in
Minimum enclosing ellipse5
Smallest enclosing annulus4
Linear programming in dimension
Distance between two polytopes
Largest ball inside a polytope

Recognising a problem as LP-type tells you immediately that a randomised incremental algorithm is .

Variants

VariantMethod
Minimum enclosing rectanglerotating calipers on the hull (Toussaint)
Minimum enclosing squarebinary search on the side + a sweep
Smallest circle covering of pointsmuch harder; or worse
Circle covering all with a fixed radiusis the intersection of the disks non-empty?
Largest empty circlea Voronoi vertex, hull edge, or bounding-box constraint
Minimum enclosing ball in 3DWelzl with dimension 4
Circle minimising the sum of distancesthe geometric median — no closed form; use Weiszfeld’s iteration or nested ternary search

The last row is worth flagging: minimising the maximum distance gives the enclosing circle (LP-type, ), while minimising the sum gives the geometric median, which is a different and harder problem with no exact algorithm.

Preprocessing with the hull

Only points on the convex hull can determine the circle, so taking the hull first reduces to . Usually not worth it — Welzl is already linear — but useful when the hull is needed anyway.

See also: Welzl’s Algorithm · Seidel’s LP · Convex Hull