All Euler problems
Project Euler

Ambiguous Numbers

A best rational approximation to a real number x for denominator bound d is a fraction r/s (fully reduced, s <= d) such that |x - r/s| < |x - p/q| for every p/q with q <= d and q ne s. A real numbe...

Source sync Apr 19, 2026
Problem #0198
Level Level 13
Solved By 1,378
Languages C++, Python
Answer 52374425
Length 1,782 words
modular_arithmeticnumber_theoryanalytic_math

Problem Statement

This archive keeps the full statement, math, and original media on the page.

A best approximation to a real number \(x\) for the denominator bound \(d\) is a rational number \(\frac r s\) (in reduced form) with \(s \le d\), so that any rational number \(\frac p q\) which is closer to \(x\) than \(\frac r s\) has \(q > d\).

Usually the best approximation to a real number is uniquely determined for all denominator bounds. However, there are some exceptions, e.g. \(\frac 9 {40}\) has the two best approximations \(\frac 1 4\) and \(\frac 1 5\) for the denominator bound \(6\). We shall call a real number \(x\) ambiguous, if there is at least one denominator bound for which \(x\) possesses two best approximations. Clearly, an ambiguous number is necessarily rational.

How many ambiguous numbers \(x=\frac p q, 0 < x < \frac 1 {100}\), are there whose denominator \(q\) does not exceed \(10^8\)?

Problem 198: Ambiguous Numbers

Mathematical Foundation

Theorem 1. (Midpoint Characterization.) A rational number xx is ambiguous if and only if xx is the midpoint of two Farey neighbours. That is, x=a/b+c/d2x = \frac{a/b + c/d}{2} where a/b<c/da/b < c/d are adjacent fractions in some Farey sequence FDF_D (meaning bcad=1bc - ad = 1).

Proof. (\Rightarrow) If xx is ambiguous, there exists a bound DD such that two distinct fractions r1/s1r_1/s_1 and r2/s2r_2/s_2 (both with denominator D\leq D) are equidistant from xx and closer than all others. By the properties of best approximations, these two fractions must be adjacent in FDF_D (no fraction with denominator D\leq D lies between them), and xx is their midpoint.

(\Leftarrow) If x=(a/b+c/d)/2x = (a/b + c/d)/2 with bcad=1bc - ad = 1, then for D=max(b,d)1D = \max(b, d) - 1 (or more precisely, for the largest DD such that no Farey fraction lies strictly between a/ba/b and c/dc/d), both a/ba/b and c/dc/d are equidistant from xx, making xx ambiguous. \square

Theorem 2. (Lowest-Terms Form of Midpoints.) For Farey neighbours a/b<c/da/b < c/d with bcad=1bc - ad = 1, their midpoint is

x=ad+bc2bd=2ad+12bd,x = \frac{ad + bc}{2bd} = \frac{2ad + 1}{2bd},

and this fraction is already in lowest terms, i.e., gcd(2ad+1,2bd)=1\gcd(2ad + 1, 2bd) = 1, so the denominator in lowest terms is q=2bdq = 2bd.

Proof. Since bcad=1bc - ad = 1, we have ad+bc=ad+(ad+1)=2ad+1ad + bc = ad + (ad + 1) = 2ad + 1, which is odd. Thus gcd(2ad+1,2)=1\gcd(2ad + 1, 2) = 1.

For gcd(2ad+1,b)\gcd(2ad + 1, b): since ad1(modb)ad \equiv -1 \pmod{b} (from bcad=1bc - ad = 1 and bc0(modb)bc \equiv 0 \pmod{b}), we get 2ad+12+1=1(modb)2ad + 1 \equiv -2 + 1 = -1 \pmod{b}, so gcd(2ad+1,b)=gcd(1,b)=1\gcd(2ad + 1, b) = \gcd(-1, b) = 1.

For gcd(2ad+1,d)\gcd(2ad + 1, d): since bc1(modd)bc \equiv 1 \pmod{d}, we get ad+bc0+1=1(modd)ad + bc \equiv 0 + 1 = 1 \pmod{d}, so gcd(2ad+1,d)=gcd(1,d)=1\gcd(2ad + 1, d) = \gcd(1, d) = 1.

Therefore gcd(2ad+1,bd)=1\gcd(2ad + 1, bd) = 1, and since gcd(2ad+1,2)=1\gcd(2ad + 1, 2) = 1, we have gcd(2ad+1,2bd)=1\gcd(2ad + 1, 2bd) = 1. \square

Lemma 1. (Counting Reduction.) The condition qN=108q \leq N = 10^8 becomes bdN/2bd \leq N/2. For each coprime pair (b,d)(b, d) with b1b \geq 1, d1d \geq 1, bdN/2bd \leq N/2, there is a unique a{0,1,,b1}a \in \{0, 1, \ldots, b-1\} satisfying ad1(modb)ad \equiv -1 \pmod{b} (namely a=bd1modba = b - d^{-1} \bmod b). This gives a midpoint x[0,1)x \in [0, 1).

Proof. Since gcd(b,d)=1\gcd(b, d) = 1, the inverse d1modbd^{-1} \bmod b exists and is unique. The value a=(d1)modba = (-d^{-1}) \bmod b is the unique solution in {0,,b1}\{0, \ldots, b-1\}. \square

Lemma 2. (Range Constraint.) The midpoint x=(2ad+1)/(2bd)x = (2ad+1)/(2bd) lies in (0,1/100)(0, 1/100) if and only if

0<a<b10012d.0 < a < \frac{b}{100} - \frac{1}{2d}.

For b<100b < 100, the only possibility is a=0a = 0, which gives x=1/(2bd)<1/100x = 1/(2bd) < 1/100 iff bd>50bd > 50.

Proof. x>0x > 0 requires a0a \geq 0 (with a=0a = 0 giving x=1/(2bd)x = 1/(2bd)). x<1/100x < 1/100 requires 2ad+1<2bd/1002ad + 1 < 2bd/100, i.e., a<b/1001/(2d)a < b/100 - 1/(2d). For b<100b < 100, the bound b/100<1b/100 < 1 forces a=0a = 0. \square

Editorial

A best approximation to x for denominator bound d is r/s (fully reduced, s<=d) such that |x - r/s| < |x - p/q| for any p/q with q <= d and q != s. x is ambiguous if for some d, there are two or more best approximations. Key insight: x = p/q (in lowest terms) is ambiguous iff there exist Farey neighbours a/b, c/d with bc-ad=1 such that x is equidistant from them: |x - a/b| = |x - c/d|, i.e., x = (a/b + c/d) / 2 = (ad+bc)/(2bd). We need p/q = (ad+bc)/(2bd) in lowest terms, q <= 10^8, 0 < p/q < 1/100. Since bc - ad = 1: ad + bc = 2ad + 1 (because bc = ad + 1). So x = (2ad+1)/(2bd), and since 2ad+1 is odd and gcd(2ad+1, 2bd) needs computation: gcd(2ad+1, 2bd): since 2ad+1 is odd, = gcd(2ad+1, bd). gcd(2ad+1, b): since ad ≡ -1 (mod b), 2ad+1 ≡ -2+1 = -1 (mod b), so gcd = gcd(1,b) = 1. gcd(2ad+1, d): since bc-ad=1, bc ≡ 1 (mod d) (if d > 1), so ad = bc-1 ≡ -1 (mod d). 2ad+1 ≡ -2+1 = -1 (mod d). So gcd(2ad+1, d) = gcd(1, d) = 1. Wait: -1 mod d. gcd(-1, d) = gcd(d-1, d) = 1 for d > 1. For d = 1: gcd(anything, 1) = 1. So yes, gcd = 1. So gcd(2ad+1, bd) = 1, meaning the fraction (2ad+1)/(2bd) is already in lowest terms. Denominator = 2bd. Need 2bd <= N = 10^8, i.e., bd <= 510^7. So the problem reduces to: Count pairs (b, d) with b, d >= 1, gcd(b,d) = 1, bd <= N/2 = 510^7, and there exists a with 0 <= a, bc-ad=1 (c=(1+ad)/b), such that 0 < (2ad+1)/(2bd) < 1/100. For the existence of a: since gcd(b,d)=1, the equation bc-ad=1 always has solutions. The solution a is unique mod b: a ≡ -d^{-1} (mod b), a in [0, b). (But different choices of a give midpoints differing by integers, so only one is in [0,1).) Midpoint = (2ad+1)/(2bd). Condition 0 < midpoint: always true since 2ad+1 >= 1. Condition midpoint < 1/100: 100(2ad+1) < 2bd => 100ad + 50 < bd => a < (bd-50)/(100d) = b/100 - 1/(2d). For the canonical a in [0, b): a ≡ -d^{-1} (mod b). We need a < b/100 - 1/(2d). Special cases: Count: number of d with d > 50, d <= N/2, gcd(1,d)=1 = N/2 - 50. Need a < 2/100 = 0.02. But a = 1 >= 0.02. No solutions. Since a ≡ -d^{-1} (mod b), we need to count d with d <= N/(2b), gcd(d,b)=1, and (-d^{-1} mod b) < b/100 - 1/(2d). Now the pair (b,d) is unordered in the sense that (b,d) and (d,b) give different midpoints (since a depends on the specific b,d). So we count all ordered pairs. Actually wait - I need to reconsider. The Farey neighbors a/b < c/d give a specific midpoint. But (a/b, c/d) and (c/d, a/b) don’t make sense since we need a/b < c/d. However, for a pair of denominators (b, d), the Farey neighbors can have b as the left or right denominator. So we should count both (b, d, a) and (d, b, a’) as potentially giving different midpoints. Hmm, actually each unordered coprime pair {b, d} gives rise to TWO ordered pairs of Farey neighbors: one with a/b < c/d and one with c’/d < a’/b. Wait, no. For Farey neighbors a/b, c/d with bc-ad=1, b and d are the denominators. If we swap: a’/d, c’/b with ba’ - dc’ = 1… this is a different pair giving a different midpoint. So we should count all ORDERED coprime pairs (b, d) with bd <= N/2. Let me verify with the b=1 case. Need (2d-1)/(2d) < 1/100: 100(2d-1) < 2d => 200d - 100 < 2d => 198d < 100. d = 0 (invalid) or d < 100/198 < 1. No solutions. So ordered pairs are correct and (d, 1) gives midpoints near 1, not near 0. So the answer = (N/2 - 50) + sum_{b=100}^{N/2} C(b) where C(b) = #{d : 1 <= d <= N/(2b), gcd(d,b)=1, a(b,d) < b/100 - 1/(2d)} and a(b,d) = (-d^{-1}) mod b. For the sum: iterate b from 100 to N/2, and for each b compute C(b). Approach for computing C(b): i.e., 1/(2d) < b/100 - a, i.e., d > 1/(2(b/100 - a)) = 50/(b - 100a) For each (b, a), the count of valid d is: d_0 = (-a^{-1}) mod b (in [1, b]) d_min = ceil(50/(b - 100a)) + 1 if 50/(b-100a) is integer, else ceil(50/(b-100a)) Actually: d > 50/(b - 100a), so d >= floor(50/(b-100a)) + 1. d_max = N // (2b) Count = max(0, (d_max - first_valid_d) // b + 1) where first_valid_d is the smallest d >= d_min with d ≡ d_0 (mod b). The total work: sum_{b=100}^{N/2} (number of valid a for b) ≈ sum_{b=100}^{N/2} phi(b)/100 ≈ (1/100) * sum phi(b) ≈ (1/100) * 3*(N/2)^2/pi^2 ≈ 1.5 * 10^12. Too slow! We need to split into small-b and large-b. For b <= B (B = sqrt(N/2) ≈ 7071): d_max = N/(2b) >= B. Many d per (b,a). Iterate a, compute count. Work per b: b/100 (number of a values). Total: sum_{b=100}^{B} b/100 ≈ B^2/200 ≈ 250000. For b > B: d_max = N/(2b) < B. Few d values. Swap: iterate d from 1 to B, then for each d iterate b. For each d, b ranges over [max(B+1, 100), N/(2d)]. For each (d, b), check a(b,d) < b/100 - 1/(2d). But b ranges up to N/(2d) which can be up to 510^7. Too many. Better: for each d, b must be coprime to d and have a specific a. a = (-d^{-1}) mod b depends on b, so we can’t simply batch. Alternative for large b: use the fact that the valid a values are few. For b > B, a < b/100. But a ≡ -d^{-1} (mod b). So we need to find, for each (residue class mod b), how many d in [1, d_max] satisfy it. Since d_max < B < b, there’s at most 1 valid d per a. So for b > B: d = ((-a^{-1}) mod b), and check 1 <= d <= d_max and d > 50/(b-100a). Total work: sum_{b=B+1}^{N/2} (b/100) … still too much. Hmm. For b > B and d < B: swap iteration to d outer, b inner. For each d from 1 to B: For each b in [B+1, N/(2d)] with gcd(b,d)=1: a = (-d^{-1}) mod b Check a < b/100 - 1/(2d) But this iterates over all b, which is up to N/(2d). Total: sum_{d=1}^{B} N/(2d) ≈ N/2 * ln(B) ≈ 510^7 * 9 ≈ 4.510^8. Borderline. Can optimize: for fixed d, the condition a < b/100 - 1/(2d) where a = (-d^{-1}) mod b. Note: a = b - (d^{-1} mod b) (if d^{-1} mod b > 0). d^{-1} mod b: this is the unique r in [1, b-1] with dr ≡ 1 (mod b). a = b - r = b - (d^{-1} mod b). Condition: b - r < b/100 - 1/(2d) => b(1 - 1/100) < r + … => r > 99b/100 + 1/(2d). i.e., r > 99b/100 (approximately). So r/b > 99/100, meaning d^{-1}/b is close to 1, i.e., d^{-1} ≡ r (mod b) with r > 99b/100. Equivalently dr ≡ 1 (mod b) and r > 99b/100. This means dr = 1 + kb for some k, and r > 99b/100. So 1 + kb = dr > d99b/100 => k > 99d/100 - 1/b. For d >= 2: k >= 1. Also r = (1+kb)/d <= b-1 => 1+kb <= d(b-1) => k <= (d(b-1)-1)/b < d. And k > 99d/100 - 1/b. For large d, k is near d, so k in [ceil(99d/100), d-1]. For each valid k: b = (dr - 1)/k. And r = (1+kb)/d. Hmm, circular. Let me try: dr - kb = 1. So for each k, r = (1 + kb)/d must be integer. 1 + kb ≡ 0 (mod d) => kb ≡ -1 (mod d) => b ≡ -k^{-1} (mod d) (when gcd(k,d)=1). This is getting too complicated. Let me just implement the straightforward O(N^(1+eps)) algorithm and accept that it might take some time. For the problem, I’ll use a combined approach: 1) b = 1: counted analytically 2) small b (100 to B): iterate a for each b 3) large b (B+1 to N/2): iterate d from 1 to B, b per d. We case 1: b = 1, a = 0, x = 1/(2d), need d > 50. We then case 2: b >= 2, small b (b <= sqrt(half_N)). Finally, iterate over b from 2 to B.

Pseudocode

Case 1: b = 1, a = 0, x = 1/(2d), need d > 50
Case 2: b >= 2, small b (b <= sqrt(half_N))
for b from 2 to B
d must be coprime to b, d <= half_N / b, d > 50/b (if a=0)
else
d ≡ -a^{-1} (mod b), d <= half_N / b
Case 3: large b (b > sqrt(half_N))
Swap iteration: for each d from 1 to B, iterate over valid b
for d from 1 to B
For each valid a, count large b in arithmetic progression mod d

Complexity Analysis

  • Time: Cases 2 and 3 each involve O(N/2)O(\sqrt{N/2}) outer iterations, with inner work proportional to O(N)O(\sqrt{N}) total. Overall: O(N)O(N) arithmetic operations.
  • Space: O(1)O(1) beyond loop variables and small lookup tables for modular inverses.

Answer

52374425\boxed{52374425}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_198/solution.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

// Problem 198: Ambiguous Numbers
//
// x = p/q is ambiguous iff it equals (ad+bc)/(2bd) for Farey neighbors a/b, c/d
// with bc-ad=1. The denominator is always 2bd in lowest terms.
// So we need bd <= N/2 and the midpoint in (0, 1/100).
//
// Split into: b=1 case, small b (100..B), and large b (B+1..N/2) via d-iteration.

ll mod_inverse(ll a, ll m) {
    // Extended Euclidean
    ll g = __gcd(a, m);
    if (g != 1) return -1;
    // Using pow with modular inverse for prime... but m may not be prime.
    // Use extended gcd.
    ll x = 1, y = 0, x1 = 0, y1 = 1, a0 = a, m0 = m;
    while (m0 != 0) {
        ll q = a0 / m0;
        ll tmp;
        tmp = x - q * x1; x = x1; x1 = tmp;
        tmp = y - q * y1; y = y1; y1 = tmp;
        tmp = a0 - q * m0; a0 = m0; m0 = tmp;
    }
    return ((x % m) + m) % m;
}

int main() {
    ios_base::sync_with_stdio(false);

    const ll N = 100000000LL;
    const ll HALF = N / 2;
    const ll B = (ll)sqrt((double)HALF);

    ll count = 0;

    // b = 1: d > 50, d <= HALF
    count += HALF - 50;

    // Small b: 100 to B
    for (ll b = 100; b <= B; b++) {
        ll d_max = HALF / b;
        if (d_max < 1) break;

        ll a_upper;
        if (b % 100 == 0)
            a_upper = b / 100 - 1;
        else
            a_upper = b / 100;

        for (ll a = 1; a <= a_upper; a++) {
            if (__gcd(a, b) != 1) continue;

            ll a_inv = mod_inverse(a, b);
            ll d_res = (b - a_inv) % b;  // (-a_inv) mod b
            if (d_res <= 0) d_res += b;

            ll gap = b - 100 * a;
            if (gap <= 0) continue;

            ll d_min = 50 / gap + 1; // strict >

            ll first_d;
            if (d_res >= d_min)
                first_d = d_res;
            else {
                ll k = (d_min - d_res + b - 1) / b;
                first_d = d_res + k * b;
            }

            if (first_d > d_max) continue;
            count += (d_max - first_d) / b + 1;
        }
    }

    // Large b: iterate d from 1 to B, then m values
    for (ll d = 1; d <= B; d++) {
        ll b_max = HALF / d;
        if (b_max <= B) continue;

        ll m_min;
        if ((99 * d) % 100 == 0)
            m_min = 99 * d / 100;
        else
            m_min = 99 * d / 100 + 1;

        ll m_max = d - 1;

        for (ll m = m_min; m <= m_max; m++) {
            if (__gcd(m, d) != 1) continue;

            ll m_inv = mod_inverse(m, d);
            ll b_res = (d - m_inv) % d; // (-m^{-1}) mod d
            if (b_res <= 0) b_res += d;

            ll first_b;
            if (b_res >= B + 1)
                first_b = b_res;
            else {
                ll k = (B + 1 - b_res + d - 1) / d;
                first_b = b_res + k * d;
            }

            if (first_b > b_max) continue;
            count += (b_max - first_b) / d + 1;
        }
    }

    cout << count << endl;
    return 0;
}