All Euler problems
Project Euler

Best Approximations

For each non-perfect-square integer d with 2 <= d <= 100000, find the best rational approximation p/q to sqrt(d) with q <= 10^12. Here "best" means no other fraction with denominator at most 10^12...

Source sync May 21, 2026
Problem #0192
Level Level 11
Solved By 1,979
Languages C++, Python
Answer 57060635927998347
Length 477 words
sequenceanalytic_mathalgebra

Problem Statement

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

Let $x$ be a real number.

A best approximation to $x$ for the denominator bound $d$ is a rational number $\frac r s $ in reduced form, with $s \le d$, such that any rational number which is closer to $x$ than $\frac r s$ has a denominator larger than $d$: $$\Big|\frac p q -x \Big| < \Big|\frac r s -x\Big| \Rightarrow q > d$$ For example, the best approximation to $\sqrt {13}$ for the denominator bound 20 is $\frac {18} 5$ and the best approximation to $\sqrt {13}$ for the denominator bound 30 is $\frac {101}{28}$.

Find the sum of all denominators of the best approximations to $\sqrt n$ for the denominator bound $10^{12}$, where $n$ is not a perfect square and $ 1 < n \le 100000$.

Problem 192: Best Approximations

Mathematical Development

Theorem 1. (Continued Fraction Expansion of Quadratic Irrationals.) For every non-square positive integer dd, d\sqrt{d} has an eventually periodic continued fraction expansion

d=[a0;a1,a2,…,aT‾]\sqrt{d} = [a_0; \overline{a_1, a_2, \ldots, a_T}]

where a0=⌊d⌋a_0 = \lfloor \sqrt{d} \rfloor and the period TT satisfies aT=2a0a_T = 2a_0. The partial quotients are computed via:

m0=0,  d0=1,  a0=⌊d⌋,m_0 = 0,\; d_0 = 1,\; a_0 = \lfloor \sqrt{d} \rfloor, mn+1=andn−mn,dn+1=d−mn+12dn,an+1=⌊a0+mn+1dn+1⌋.m_{n+1} = a_n d_n - m_n,\quad d_{n+1} = \frac{d - m_{n+1}^2}{d_n},\quad a_{n+1} = \left\lfloor \frac{a_0 + m_{n+1}}{d_{n+1}} \right\rfloor.

Proof. This is a classical result due to Lagrange. The quadratic irrational a0+mndn\frac{a_0 + m_n}{d_n} undergoes a purely periodic transformation under the continued fraction algorithm. Since dn∣(d−mn2)d_n \mid (d - m_n^2) is maintained as an invariant and 0<mn<d0 < m_n < \sqrt{d}, there are finitely many possible states, forcing periodicity. □\square

Theorem 2. (Convergent Recurrence.) The convergents hk/kkh_k/k_k of a continued fraction [a0;a1,a2,…][a_0; a_1, a_2, \ldots] satisfy

hk=akhk−1+hk−2,kk=akkk−1+kk−2,h_k = a_k h_{k-1} + h_{k-2}, \quad k_k = a_k k_{k-1} + k_{k-2},

with h−1=1,h−2=0,k−1=0,k−2=1h_{-1} = 1, h_{-2} = 0, k_{-1} = 0, k_{-2} = 1.

Proof. By induction on kk, using the matrix identity

(hkhk−1kkkk−1)=(a0110)(a1110)⋯(ak110).\begin{pmatrix} h_k & h_{k-1} \\ k_k & k_{k-1} \end{pmatrix} = \begin{pmatrix} a_0 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} a_1 & 1 \\ 1 & 0 \end{pmatrix} \cdots \begin{pmatrix} a_k & 1 \\ 1 & 0 \end{pmatrix}.

□\square

Theorem 3. (Best Approximation Theorem.) Let α\alpha be irrational with convergents hk/kkh_k/k_k. For a given bound QQ on the denominator, the best approximation to α\alpha with denominator ≤Q\leq Q is either:

  1. A convergent hk/kkh_k/k_k with kk≤Qk_k \leq Q, or
  2. A semiconvergent m⋅hk+hk−1m⋅kk+kk−1\frac{m \cdot h_{k} + h_{k-1}}{m \cdot k_{k} + k_{k-1}} for some integer 1≤m<ak+11 \leq m < a_{k+1}, where kk+1>Qk_{k+1} > Q.

Proof. This follows from the theory of best rational approximations (see Hardy & Wright, Chapter 10). Every best approximation of the second kind to α\alpha is either a convergent or a semiconvergent. The key identity is that convergents alternate in being above and below α\alpha, with

∣knα−hn∣<∣kn−1α−hn−1∣|k_n \alpha - h_n| < |k_{n-1} \alpha - h_{n-1}|

for all nn, and semiconvergents interpolate monotonically between consecutive convergents. □\square

Lemma 1. (Semiconvergent Selection Criterion.) Let α=d\alpha = \sqrt{d}, and suppose the next convergent hn/knh_n/k_n has kn>Qk_n > Q. The largest valid semiconvergent uses m=⌊(Q−kn−2)/kn−1⌋m = \lfloor (Q - k_{n-2}) / k_{n-1} \rfloor. This semiconvergent hs/ksh_s/k_s is better than the previous convergent hn−1/kn−1h_{n-1}/k_{n-1} if and only if

∣ksα−hs∣<∣kn−1α−hn−1∣.|k_s \alpha - h_s| < |k_{n-1} \alpha - h_{n-1}|.

This can be checked with exact integer arithmetic by squaring both sides and using α2=d\alpha^2 = d:

(ks2d−hs2)2⋅kn−12<(kn−12d−hn−12)2⋅ks2.(k_s^2 d - h_s^2)^2 \cdot k_{n-1}^2 < (k_{n-1}^2 d - h_{n-1}^2)^2 \cdot k_s^2.

Proof. Both ∣ksα−hs∣|k_s \alpha - h_s| and ∣kn−1α−hn−1∣|k_{n-1} \alpha - h_{n-1}| are positive (since α\alpha is irrational). Squaring preserves the inequality. Substituting α2=d\alpha^2 = d converts the comparison to integer arithmetic. The signs of kiα−hik_i \alpha - h_i alternate with the parity of the convergent index, but this does not affect the absolute value comparison. □\square

Editorial

Continued fractions are the right tool here because every best approximation to sqrt(d) with a denominator cap comes from the convergents or the semiconvergents between two consecutive convergents. For each non-square d, we expand the continued fraction only until the next convergent denominator would exceed 10^12. At that moment there are only two serious candidates left: the previous convergent, and the last semiconvergent that still respects the bound.

The implementation keeps the continued-fraction state and the last two convergent denominators. Once the next denominator is too large, it computes the largest admissible semiconvergent multiplier and uses the exact inequality derived from the complete quotient to decide whether that semiconvergent is closer than the previous convergent. That gives the correct denominator for one d, and the global answer is just the sum over all 2 <= d <= 100000 that are not perfect squares.

Pseudocode

Set the denominator bound Q to 10^12 and the running total to 0.

For each d from 2 to 100000:
    Skip d if it is a perfect square.
    Initialize the continued-fraction state for sqrt(d).
    Track the last two convergent denominators.

    Repeatedly generate the next partial quotient and next convergent denominator.
    When the next denominator would exceed Q:
        let t be the largest multiplier that keeps a semiconvergent within Q;
        compare that semiconvergent against the previous convergent
        with the exact integer test coming from the complete quotient;
        add the winning denominator to the total;
        stop processing this d.

Return the total.

Complexity Analysis

  • Time: For each non-square dd, the continued fraction period is O(d)O(\sqrt{d}) in the worst case. Summing over d≤Nd \leq N: O(N3/2)O(N^{3/2}) total. For N=105N = 10^5, this is approximately 3×1073 \times 10^7 operations.
  • Space: O(1)O(1) per value of dd (streaming computation), plus O(N)O(\sqrt{N}) for the perfect-square sieve.

Answer

57060635927998347\boxed{57060635927998347}

Code

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

C++ project_euler/problem_192/solution.cpp
#include <cassert>
#include <cmath>
#include <iostream>

using namespace std;

const unsigned long long BASE = 1000000000ULL;

struct Big4 {
    unsigned long long limb[4] = {0, 0, 0, 0};
};

Big4 square_u64(unsigned long long value) {
    unsigned long long low = value % BASE;
    unsigned long long high = value / BASE;
    Big4 result;

    unsigned long long term0 = low * low;
    result.limb[0] = term0 % BASE;
    unsigned long long carry = term0 / BASE;

    unsigned long long term1 = 2ULL * low * high + carry;
    result.limb[1] = term1 % BASE;
    carry = term1 / BASE;

    unsigned long long term2 = high * high + carry;
    result.limb[2] = term2 % BASE;
    result.limb[3] = term2 / BASE;
    return result;
}

void multiply_small(Big4& value, unsigned long long factor) {
    unsigned long long carry = 0;
    for (int i = 0; i < 4; ++i) {
        unsigned long long current = value.limb[i] * factor + carry;
        value.limb[i] = current % BASE;
        carry = current / BASE;
    }
}

int compare_big(const Big4& left, const Big4& right) {
    for (int i = 3; i >= 0; --i) {
        if (left.limb[i] < right.limb[i]) {
            return -1;
        }
        if (left.limb[i] > right.limb[i]) {
            return 1;
        }
    }
    return 0;
}

long long best_denominator(int d, long long bound) {
    long long a0 = static_cast<long long>(sqrt(static_cast<long double>(d)));
    while ((a0 + 1) * (a0 + 1) <= d) {
        ++a0;
    }
    while (a0 * a0 > d) {
        --a0;
    }
    if (a0 * a0 == d) {
        return 0;
    }

    long long m = 0;
    long long den = 1;
    long long a = a0;
    long long q_prev2 = 1;
    long long q_prev1 = 0;

    while (true) {
        long long q_curr = a * q_prev1 + q_prev2;
        if (q_curr > bound) {
            long long t = (bound - q_prev2) / q_prev1;
            if (t <= 0) {
                return q_prev1;
            }

            long long rhs_value = den * (2LL * t * q_prev1 + q_prev2) - m * q_prev1;
            unsigned long long rhs = static_cast<unsigned long long>(rhs_value < 0 ? -rhs_value : rhs_value);
            Big4 left = square_u64(rhs);
            Big4 right = square_u64(static_cast<unsigned long long>(q_prev1));
            multiply_small(right, static_cast<unsigned long long>(d));
            if (compare_big(left, right) > 0) {
                return t * q_prev1 + q_prev2;
            }
            return q_prev1;
        }

        q_prev2 = q_prev1;
        q_prev1 = q_curr;
        m = a * den - m;
        den = (d - m * m) / den;
        a = (a0 + m) / den;
    }
}

int main() {
    const long long bound = 1000000000000LL;
    long long total = 0;

    for (int d = 2; d <= 100000; ++d) {
        total += best_denominator(d, bound);
    }

    assert(total == 57060635927998347LL);
    cout << total << '\n';
    return 0;
}