Iterative Circle Packing
Three unit circles are packed inside a larger circle such that each is tangent to the other two and to the outer circle. In the first iteration, we fill each of the initial curvilinear triangular g...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Three circles of equal radius are placed inside a larger circle such that each pair of circles is tangent to one another and the inner circles do not overlap. There are four uncovered "gaps" which are to be filled iteratively with more tangent circles.

At each iteration, a maximally sized circle is placed in each gap, which creates more gaps for the next iteration. After \(3\) iterations (pictured), there are \(108\) gaps and the fraction of the area which is not covered by circles is \(0.06790342\), rounded to eight decimal places.
What fraction of the area is not covered by circles after \(10\) iterations?
Give your answer rounded to eight decimal places using the format x.xxxxxxxx .
Problem 199: Iterative Circle Packing
Mathematical Development
Theorem 1. (Descartes’ Circle Theorem.) Given four mutually tangent circles with curvatures (where curvature , negative for a circle containing the others internally), the following relation holds:
Proof. Let the four circles have centers and radii . For mutually externally tangent circles, . The theorem follows from the algebraic identity obtained by eliminating the center coordinates from these distance equations. Specifically, writing the tangency conditions as a system and applying the quadratic formula in the complex plane (the Descartes Circle Theorem can be proven via the complex Descartes theorem , but the curvature relation follows from the real part). A direct algebraic proof uses Heron’s formula and the cosine rule applied to the triangle of centers.
Lemma 1. (Fourth Curvature Formula.) Given three mutually tangent circles with curvatures , the curvature of the fourth mutually tangent circle (the one fitting in the interstice) is:
Proof. Solving the Descartes equation for :
Expand: where and .
Rearranging: .
By the quadratic formula: .
Now .
So (taking the root for the smaller circle, i.e., larger curvature).
Theorem 2. (Outer Circle Radius.) Three mutually tangent unit circles () inscribed in a larger circle have the outer circle radius:
Proof. The centers of the three unit circles form an equilateral triangle with side length (since each pair is tangent). The circumradius of this triangle is . The outer circle must pass through points at distance (the unit radius) beyond each center, so .
Lemma 2. (Initial Configuration.) The curvature of the outer circle is (negative for internal tangency). The initial curvilinear gaps are:
- 3 outer gaps with bounding curvatures .
- 1 inner gap with bounding curvatures .
Proof. By symmetry, the three outer gaps are equivalent (each bounded by two unit circles and the outer circle). The inner gap is bounded by all three unit circles.
Theorem 3. (Recursive Gap Splitting.) When a circle of curvature is inscribed in a gap bounded by circles of curvatures , three new gaps are created with bounding curvatures , , and .
Proof. The new circle is tangent to all three bounding circles. It divides the original curvilinear triangle into three smaller curvilinear triangles, each bounded by two of the original circles and the new circle.
Editorial
Each uncovered region is determined completely by the three circles that bound it, so the natural state is a triple of curvatures. The initial picture gives four gaps: one central gap bounded by the three unit circles, and three identical outer gaps bounded by the enclosing circle together with two unit circles. Descartes’ theorem tells us the curvature of the largest circle that can be inserted into any such gap.
After inserting that circle, the old gap disappears and three smaller gaps remain. So the algorithm is a direct recursive expansion of gaps for ten rounds while accumulating the area contributed by each new circle. Dividing the final covered area by the area of the enclosing circle, and subtracting from one, gives the uncovered fraction.
Pseudocode
Compute the outer circle radius and its curvature.
Initialize the covered area with the three unit circles.
Initialize the gap list with three outer gaps and one inner gap.
Repeat 10 iterations:
For each current gap:
use Descartes' theorem to compute the new circle curvature;
add that circle's area to the covered total;
create the three child gaps formed with the new circle.
Replace the current gap list with the child gaps.
Return 1 minus covered_area / outer_area, rounded to 8 decimal places.
Complexity Analysis
- Time: Each iteration triples the number of gaps. Starting with 4 gaps, after iterations there are gaps. Total gaps processed: . For : gap operations. Total: .
- Space: to store the current set of gaps. For : gap triples.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
// Three unit circles (curvature 1) inside an outer circle
// Outer circle radius R = 1 + 2/sqrt(3)
double R = 1.0 + 2.0 / sqrt(3.0);
double k_outer = -1.0 / R;
// Initial gaps:
// 3 outer gaps: (k_outer, 1, 1)
// 1 inner gap: (1, 1, 1)
// Each gap is a triple of curvatures
// We use Descartes' theorem: k4 = k1+k2+k3 + 2*sqrt(k1*k2+k2*k3+k3*k1)
double total_area = 3.0 * M_PI; // three unit circles
// Use a list of gap triples
vector<tuple<double, double, double>> gaps;
// 3 outer gaps
for (int i = 0; i < 3; i++)
gaps.push_back({k_outer, 1.0, 1.0});
// 1 inner gap
gaps.push_back({1.0, 1.0, 1.0});
for (int iter = 0; iter < 10; iter++) {
vector<tuple<double, double, double>> new_gaps;
for (auto& [a, b, c] : gaps) {
double k_new = a + b + c + 2.0 * sqrt(a * b + b * c + c * a);
total_area += M_PI / (k_new * k_new);
new_gaps.push_back({a, b, k_new});
new_gaps.push_back({a, c, k_new});
new_gaps.push_back({b, c, k_new});
}
gaps = move(new_gaps);
}
double outer_area = M_PI * R * R;
double fraction = 1.0 - total_area / outer_area;
cout << fixed << setprecision(8) << fraction << endl;
return 0;
}
"""
Problem 199: Iterative Circle Packing
Use Descartes' theorem to expand the four initial gaps for ten iterations and
track the total covered area.
"""
import math
def solve(iterations=10):
outer_radius = 1.0 + 2.0 / math.sqrt(3.0)
outer_curvature = -1.0 / outer_radius
covered_area = 3.0 * math.pi
gaps = [(outer_curvature, 1.0, 1.0)] * 3 + [(1.0, 1.0, 1.0)]
for _ in range(iterations):
next_gaps = []
for a, b, c in gaps:
new_curvature = a + b + c + 2.0 * math.sqrt(a * b + b * c + c * a)
covered_area += math.pi / (new_curvature * new_curvature)
next_gaps.append((a, b, new_curvature))
next_gaps.append((a, c, new_curvature))
next_gaps.append((b, c, new_curvature))
gaps = next_gaps
outer_area = math.pi * outer_radius * outer_radius
return 1.0 - covered_area / outer_area
if __name__ == "__main__":
assert f"{solve(3):.8f}" == "0.06790342"
print(f"{solve(10):.8f}")