Farey sequence
The Farey sequence F_n is every reduced fraction
in [0, 1] whose denominator is at most n, in increasing order. F_5 is
0/1, 1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5, 1/1. Problems reach for it whenever they say
"fractions with bounded denominator", which is more often than it sounds: the nearest fraction to a
target (71), the fractions in an interval (73), best rational approximations (198), the minimum of
{nx} over n ≤ N (965). What makes F_n useful in code is that consecutive terms are tied by an
exact integer identity. Because of that identity I can walk the sequence, jump to a neighbour, or
count a range without ever listing the fractions or calling gcd.
Neighbours and the mediant
Two fractions a/b < c/d are adjacent in some Farey sequence exactly when
b·c − a·d = 1
Everything below follows from that one line.
- The gap is
1/(bd). Subtract the fractions and the numerator is the determinant, which is 1. Nothing of denominator≤ max(b, d)fits inside a gap this small. - The first newcomer is the mediant. As
ngrows, the first fraction to land between two neighbours is their mediant(a + c)/(b + d). Adding numerators to numerators is wrong as fraction arithmetic, but here it gives exactly the right point. The mediant forms a neighbour pair with each parent (both determinants are still 1), and it shows up at orderb + dand no earlier. So two neighbours inF_nstay neighbours untilnreachesb + d. - The next term has a closed form. Given consecutive
a/b, c/dinF_n, the next term is(k·c − a)/(k·d − b)withk = ⌊(n + b)/d⌋. Herekis how many mediant steps towardc/dfit under the denominator cap. That gives a constant-memory walker over the whole sequence:
def farey(n: int):
a, b, c, d = 0, 1, 1, n
yield a, b
while c <= n:
k = (n + b) // d
a, b, c, d = c, d, k * c - a, k * d - b
yield a, b
The sequence has 1 + Σ_{q ≤ n} φ(q) ≈ 3n²/π² terms (each denominator q contributes its
totient worth of numerators). Walking it is quadratic in n. That's
fine at n = 10⁴, and at n = 10⁶ it's time to count instead of enumerate.
Problem 71: one neighbour, no walk
Problem 71 asks for the fraction immediately left of 3/7 in F_{10⁶}. Walking
a sequence of about 3·10¹¹ terms to reach one point is out of the question. The neighbour
identity skips the walk. The left neighbour p/q must satisfy 3q − 7p = 1, so its distance to
3/7 is
3/7 − p/q = 1/(7q)
Closest means the largest admissible q: the largest q ≤ N with 3q ≡ 1 (mod 7), i.e.
q ≡ 5 (mod 7). Then p = (3q − 1)/7. For N = 8 that gives q = 5, p = 2, which is the 2/5
from the statement. It's O(1), it works for any target a/b (solve aq ≡ −1 (mod b) with a
modular inverse), and the result is already reduced, because the determinant identity forbids a
common factor.
The pitfall is to take q as the largest multiple of 7 and write 3/7 − 1/(7⌊N/7⌋). That
fraction lies left of 3/7, but it isn't adjacent, because its denominator fails the congruence.
At N = 20 the true neighbour is 8/19 and the shortcut gives 5/14. Worse, at N = 8 and
N = 10⁶ the two fractions share a numerator (2/5 against 2/7, 428570/999997 against
428570/999999), so a test that only checks the numerator passes either one. Check the
determinant, not just the answer.
Problem 73: three ways to count a range
Problem 73 counts the fractions strictly between 1/3 and 1/2 in
F_{12000}. The three solutions are three readings of the same structure, and they show the cost
model clearly.
-
Walk the neighbours (
s0). Start at the term just after1/3and apply the next-term recurrence until you reach1/2. Both boundaries have numerator 1, so the numerators are determined by the denominators and the loop tracks onlyd:d_next = N − (N + d_prev) mod d. That isO(answer). -
Split by mediants (
s1). Count(L, R)as zero if the mediant's denominator exceedsN, otherwise as one plus the counts of the two halves. Again only denominators matter:def count(lo: int, hi: int) -> int: m = lo + hi return 0 if m > N else 1 + count(lo, m) + count(m, hi)Also
O(answer), with recursion depth up to aboutN. -
Count by rank (
s2).rank(x)is the number of terms ofF_Nthat are≤ x, and the answer isrank(1/2) − rank(1/3) − 1. Rank needs no enumeration.⌊q·x⌋counts every fraction≤ xwith denominatorq, reduced or not. Möbius inversion, done as a subtractive sieve over multiples, removes the non-reduced ones. That isO(N log N).
The answer grows like N², so the first two are quadratic and the third is nearly linear. At
N = 10⁵ the notes measure tens of seconds in Python for the recursion against a tenth of a second
for the sieve. My rule: walk the sequence when each term contributes something different; rank it
when you only need how many.
The Stern–Brocot tree: every Farey sequence at once
Take the mediant of every neighbour pair, again and again, starting from 0/1 and 1/0. The
fractions this generates form the Stern–Brocot tree: a binary search tree
that holds every positive rational exactly once, already in lowest terms. F_n is that tree (the
[0, 1] half) cut off at denominator n and read in order. Each node is the mediant of the two
nearest ancestors that bracket it, and the path from the root down to p/q spells
out its continued fraction. So the 73 recursion above is a depth-first
traversal of the tree, and the 965 walker that follows advances through it one in-order step at a
time.
Two private problems are really questions about this tree.
- Problem 198 (Ambiguous Numbers) counts fractions below
1/100that have two equally good best approximations for some denominator bound. The characterisation that cracks it:xis ambiguous exactly when it is the midpoint of a neighbour paira/b, c/d, so both ends are at distance1/(2bd). Because the determinant is 1, that midpoint(2ad + 1)/(2bd)is already reduced, which turns the bound onx's denominator into the clean conditionbd ≤ Q/2. The search over values becomes an enumeration of neighbour pairs. Fix the smaller denominator (at most√(Q/2)of them), and the partner denominators form one arithmetic progression, pinned by a modular inverse, which can be counted inO(1). The whole thing isO(√Q). - Problem 965 (Expected Minimal Fractional Value) integrates
f_N(x) = min_{n ≤ N} {nx}over[0, 1]. Since{nx} = n·(x − p/n), this is a weighted one-sided rational approximation, and the Farey gaps are where it becomes linear. On a gap(a/b, c/d)ofF_Nthe minimum is always attained by the left endpoint,f_N(x) = bx − a, a ramp from 0 up to1/d. The integral is then a sum of triangle areas, one per gap, collected by the constant-memory walker above. A function that looks hopeless pointwise becomes one closed-form term per neighbour pair.
The unsolved problems on the list follow the same pattern. In Problem 787 a
legal move is defined by ad − bc = ±1, which is the neighbour identity, so the game is played on
the tree. In Problem 604 a strictly convex lattice path needs edge vectors with
distinct slopes, the cheapest of which are coprime pairs, i.e. Farey fractions taken in slope order.
How to reason about it
- Spot it from the wording. "Reduced fraction", "denominator at most N", "closest fraction",
"best approximation", or a determinant
±1in the statement all point toF_n. - Use the identity, not
gcd.bc − ad = 1gives you reducedness, gap width, the next term, and the mediant, all in exact integer arithmetic. - Pick the cost model up front. One neighbour:
O(1)via a congruence. A sum of something per gap: walk inO(n²). A count: rank with a Möbius sieve inO(n log n). Pairs under a product bound: enumerate the smaller denominator and count the progression,O(√Q). - Recursion over mediants is elegant but deep. The tree's left spine has length
n. Python needs a raised recursion limit and pays per call, while C handles it comfortably. Prefer the iterative walker when the depth tracksn.