geometry
circle
minimal enclosing circle
computational geometry
algorithms

How can I find the minimal circle include some given points?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The problem of finding the smallest circle that contains a set of points is the minimal enclosing circle problem. It appears in collision detection, clustering, geofencing, and computational geometry. The key geometric fact is that the final circle is determined by at most three boundary points.

The Geometric Idea

The minimal enclosing circle is unique for a finite set of points in the plane. Its boundary is defined by either:

  • two points, which form a diameter
  • three points, which determine a circumcircle

That observation is what makes efficient algorithms possible. You do not need to search over arbitrary circles; you only need to consider circles induced by boundary points.

A Simple Incremental Strategy

A practical algorithm is randomized incremental construction, often associated with Welzl's algorithm. The idea is:

  1. shuffle the points
  2. build the circle incrementally
  3. whenever a point lies outside the current circle, rebuild the circle with that point forced onto the boundary

A straightforward Python implementation follows this pattern.

python
1import math
2import random
3
4
5def dist(a, b):
6    return math.hypot(a[0] - b[0], a[1] - b[1])
7
8
9def is_in_circle(point, circle):
10    cx, cy, r = circle
11    return dist(point, (cx, cy)) <= r + 1e-9
12
13
14def circle_from_two_points(a, b):
15    cx = (a[0] + b[0]) / 2.0
16    cy = (a[1] + b[1]) / 2.0
17    r = dist(a, b) / 2.0
18    return (cx, cy, r)
19
20
21def circle_from_three_points(a, b, c):
22    ax, ay = a
23    bx, by = b
24    cx, cy = c
25
26    d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by))
27    if abs(d) < 1e-12:
28        return None
29
30    ux = ((ax * ax + ay * ay) * (by - cy) +
31          (bx * bx + by * by) * (cy - ay) +
32          (cx * cx + cy * cy) * (ay - by)) / d
33    uy = ((ax * ax + ay * ay) * (cx - bx) +
34          (bx * bx + by * by) * (ax - cx) +
35          (cx * cx + cy * cy) * (bx - ax)) / d
36    r = dist((ux, uy), a)
37    return (ux, uy, r)
38
39
40def minimal_enclosing_circle(points):
41    pts = points[:]
42    random.shuffle(pts)
43    circle = (0.0, 0.0, 0.0)
44
45    for i, p in enumerate(pts):
46        if is_in_circle(p, circle):
47            continue
48        circle = (p[0], p[1], 0.0)
49        for j in range(i):
50            q = pts[j]
51            if is_in_circle(q, circle):
52                continue
53            circle = circle_from_two_points(p, q)
54            for k in range(j):
55                r = pts[k]
56                if is_in_circle(r, circle):
57                    continue
58                triple_circle = circle_from_three_points(p, q, r)
59                if triple_circle is not None:
60                    circle = triple_circle
61    return circle
62
63
64points = [(0, 0), (2, 0), (1, 2), (1, 1)]
65print(minimal_enclosing_circle(points))

This is compact, practical, and good enough for many applications.

Why the Algorithm Works

When a new point already lies inside the current circle, nothing changes. When it lies outside, that point must lie on the boundary of the new minimal circle.

The nested rebuild steps reflect the same logic:

  • if one point forces an update, it belongs on the boundary
  • if a second point also lies outside, the new circle must include both on the boundary
  • if a third point still lies outside, the boundary circle is determined by three points

That structure is exactly what the geometry predicts.

Degenerate Cases

Not every three-point set defines a valid circumcircle. If the points are collinear, the circumcircle formula degenerates because the denominator goes to zero.

In that case, the minimal enclosing circle is determined by the two farthest relevant boundary points instead of a true three-point circumcircle.

That is why the code checks for an almost-zero denominator and skips invalid three-point circles.

Complexity in Practice

Welzl-style randomized incremental algorithms run in expected linear time, which is why they are popular in practice. A brute-force search over all two-point and three-point circles is conceptually simple but much slower.

For most engineering use cases, the randomized approach is the right balance between correctness and implementation complexity.

Common Pitfalls

The biggest mistake is assuming the answer must be based on all points. The minimal enclosing circle is determined by at most three boundary points.

Another mistake is ignoring floating-point tolerance. Point-in-circle checks near the boundary need a small epsilon to avoid unstable behavior.

People also forget collinear cases. Three points do not always define a usable circumcircle.

Finally, do not overcomplicate the problem with heavy optimization unless the dataset is truly large. A clear incremental implementation is often enough.

Summary

  • The minimal enclosing circle is the smallest circle containing all given points.
  • The final circle is determined by at most three boundary points.
  • A randomized incremental algorithm is a practical and efficient way to compute it.
  • Two-point diameter circles and three-point circumcircles are the key geometric building blocks.
  • Handle floating-point tolerance and collinear points carefully.

Course illustration
Course illustration

All Rights Reserved.