A Hundred Random Chords
The June 2026 MoMath Monthly Mindbender: 100 chords are drawn on a circle, each joining two independent uniform points on the circumference. Two random chords cross with probability 1/3, so the expected number of intersection points is 4950/3 = 1650. The same count fixes the variance, 22660, and the expected number of regions the disc is cut into, exactly 1751.
Problem
Choose a point uniformly at random on a circle, choose a second the same way, and join them with a chord. Do this 100 times, giving 100 random chords. What is the expected number of intersection points?
Credit: MoMath Monthly Mindbenders, June 2026.
When do two chords cross?
Everything rests on one question: what is the chance that two random chords cross? A chord pair involves four independent uniform points on the circle, all distinct with probability . Condition on where the four points sit and forget which is which. By exchangeability, every assignment of the four positions to the four labels is equally likely, so the two points forming the first chord are equally likely to be any of the three ways of splitting the four positions into two pairs. Walking round the circle through positions , the three splittings pair the positions as , , and , and only the last, the interleaved one, makes the chords cross. Hence
A second route gives the same number and will earn its keep shortly. The first chord’s endpoints cut the circumference into two arcs, and the fraction of the circle on one side is uniform on . The second chord crosses the first exactly when its two endpoints land on opposite sides, which given has probability , and
The count
The rest is linearity of expectation. With probability no two chords share an endpoint and no three pass through a common point, so the intersection points are exactly the crossing pairs of chords, and the pairs are in number. Each crosses with probability , and expectations add whether or not the events are independent (they are not), so the expected number of intersection points is
In general, random chords produce crossings on average.
How much does it fluctuate?
The mean needed no thought about dependence, but the spread does. Write , the sum over the chord pairs of the indicator that the pair crosses. Each indicator has variance . Two pairs with no chord in common involve eight independent points, so their indicators are independent and contribute nothing. The interesting case is two pairs sharing a chord, say and : given the common chord’s arc fraction , the other two chords cross it independently, each with probability , so
The covariance is positive: a chord that cuts near-diametrically across the circle is easy to cross, and one that hugs the rim is hard, so crossings sharing a chord rise and fall together. There are ordered pairs of pairs with a common chord, and altogether
a standard deviation of . The shared-chord terms dominate the variance twenty to one, yet the count still behaves like a textbook normal, as the histogram shows.
A remark on regions
The crossing count is doing more work than it appears to. Draw the chords one at a time: a new chord that meets of the earlier ones passes through of the existing regions and splits each, adding regions. Starting from one region and summing, the disc ends up cut into exactly pieces. So the expected number of regions the chords carve the disc into is
an exact consequence of the count just computed, not a separate calculation.
Checking it
The check replays the construction with no shortcuts: draw the endpoint angles, test every pair of chords for interleaving, and count. The pair test also confirms the two probabilities the derivation leans on, for one crossing and for two crossings of a shared chord.
import numpy as np
rng = np.random.default_rng(7)
n, T, C = 100, 50_000, 100 # chords, trials, chunk
iu = np.triu_indices(n, 1)
counts = []
for _ in range(T // C):
th = rng.random((C, n, 2)) # endpoint angles (turns)
a, b = th[..., 0], th[..., 1]
L = (b - a) % 1.0 # arc from a round to b
p = (a[:, None, :] - a[:, :, None]) % 1.0 # j's endpoints seen from a_i
q = (b[:, None, :] - a[:, :, None]) % 1.0
cross = (p < L[:, :, None]) ^ (q < L[:, :, None])
counts.append(cross[:, iu[0], iu[1]].sum(axis=1))
X = np.concatenate(counts)
print(f"mean {X.mean():.2f} (theory {100*99/6:.0f})")
print(f"var {X.var():.0f} (theory {100*99/9 + 100*99*98/45:.0f})")
print(f"sd {X.std():.1f} (theory {np.sqrt(100*99/9 + 100*99*98/45):.1f})")
# pairwise checks: P(cross) = 1/3, P(two chords both cross a third) = 2/15
th = rng.random((2_000_000, 3, 2)); a, b = th[..., 0], th[..., 1]
L0 = (b[:, 0] - a[:, 0]) % 1.0
def crosses(k):
pk = (a[:, k] - a[:, 0]) % 1.0; qk = (b[:, k] - a[:, 0]) % 1.0
return (pk < L0) ^ (qk < L0)
c1, c2 = crosses(1), crosses(2)
print(f"P(cross) {c1.mean():.5f} (1/3 = {1/3:.5f}); "
f"P(both cross same chord) {np.mean(c1 & c2):.5f} (2/15 = {2/15:.5f})")
# mean 1648.71 (theory 1650)
# var 22698 (theory 22660)
# sd 150.7 (theory 150.5)
# P(cross) 0.33276 (1/3 = 0.33333); P(both cross same chord) 0.13302 (2/15 = 0.13333)