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 Apr 19, 2026
Problem #0192
Level Level 11
Solved By 1,979
Languages C++, Python
Answer 57060635927998347
Length 368 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 Foundation

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=da_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=andnmn,dn+1=dmn+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(dmn2)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=akhk1+hk2,kk=akkk1+kk2,h_k = a_k h_{k-1} + h_{k-2}, \quad k_k = a_k k_{k-1} + k_{k-2},

with h1=1,h2=0,k1=0,k2=1h_{-1} = 1, h_{-2} = 0, k_{-1} = 0, k_{-2} = 1.

Proof. By induction on kk, using the matrix identity

(hkhk1kkkk1)=(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 kkQk_k \leq Q, or
  2. A semiconvergent mhk+hk1mkk+kk1\frac{m \cdot h_{k} + h_{k-1}}{m \cdot k_{k} + k_{k-1}} for some integer 1m<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<kn1αhn1|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=(Qkn2)/kn1m = \lfloor (Q - k_{n-2}) / k_{n-1} \rfloor. This semiconvergent hs/ksh_s/k_s is better than the previous convergent hn1/kn1h_{n-1}/k_{n-1} if and only if

ksαhs<kn1αhn1.|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:

(ks2dhs2)2kn12<(kn12dhn12)2ks2.(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 kn1αhn1|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

For each non-square d <= 100000, find best rational approximation p/q to sqrt(d) with q <= 10^12. Sum all q. We generate continued fraction and convergents. We then actually use standard initialization. Finally, loop.

Pseudocode

Generate continued fraction and convergents
Actually use standard initialization:
loop
Check semiconvergent
Compare |ks*sqrt(d) - hs| vs |k1*sqrt(d) - h1|
for d from 2 to 100000

Complexity Analysis

  • Time: For each non-square dd, the continued fraction period is O(d)O(\sqrt{d}) in the worst case. Summing over dNd \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 <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef __int128 lll;
typedef unsigned long long ull;

// Integer square root
ll isqrt(ll n) {
    ll x = (ll)sqrt((double)n);
    while (x * x > n) x--;
    while ((x+1)*(x+1) <= n) x++;
    return x;
}

ll best_denominator(ll d, ll Q) {
    ll a0 = isqrt(d);
    if (a0 * a0 == d) return 0; // perfect square, skip

    // Continued fraction expansion of sqrt(d)
    // We track convergents h2/k2 (two steps back), h1/k1 (one step back)
    ll m = 0, dd = 1, a = a0;
    ll h2 = 0, h1 = 1, k2 = 1, k1 = 0;

    // Process a0
    ll h = a0 * h1 + h2;
    ll k = a0 * k1 + k2;
    h2 = h1; h1 = h;
    k2 = k1; k1 = k;

    while (true) {
        m = a * dd - m;
        dd = (d - m * m) / dd;
        a = (a0 + m) / dd;

        ll knext = a * k1 + k2;
        if (knext > Q) {
            // The full convergent exceeds Q.
            // Check semiconvergent with max valid m_sc
            ll m_sc = (Q - k2) / k1;
            if (m_sc < 1) return k1; // no valid semiconvergent

            ll qs = m_sc * k1 + k2;
            ll ps = m_sc * h1 + h2;

            // Compare |qs*sqrt(d) - ps| vs |k1*sqrt(d) - h1|
            // |qs*sqrt(d) - ps|^2 = qs^2*d - 2*ps*qs*sqrt(d) + ps^2
            // But we can use: for convergent p_{n-1}/q_{n-1}, the error is
            // 1/(q_{n-1}*(a_n*q_{n-1}+q_{n-2})) approximately.
            // More precisely:
            // |q*sqrt(d)-p| compared by squaring is tricky due to sqrt.
            //
            // Instead use: semiconvergent is better iff
            //   |qs*sqrt(d) - ps| < |k1*sqrt(d) - h1|
            // Both sides positive (after abs). Square both sides:
            //   (qs^2*d + ps^2 - 2*ps*qs*sqrt(d)) < (k1^2*d + h1^2 - 2*h1*k1*sqrt(d))
            // Let A = qs^2*d + ps^2 - k1^2*d - h1^2
            // Let B = 2*(ps*qs - h1*k1)
            // We need A < B*sqrt(d)
            // If B > 0: A < B*sqrt(d) iff A < 0 or A^2 < B^2*d
            // If B <= 0: A < B*sqrt(d) iff A < 0 and A^2 > B^2*d

            lll A = (lll)qs*qs*d + (lll)ps*ps - (lll)k1*k1*d - (lll)h1*h1;
            lll B = 2*((lll)ps*qs - (lll)h1*k1);

            bool semi_better;
            if (B > 0) {
                if (A <= 0) semi_better = true;
                else semi_better = (A * A < B * B * d);
            } else {
                if (A >= 0) semi_better = false;
                else semi_better = (A * A > B * B * d);
            }

            return semi_better ? qs : k1;
        }

        ll hnext = a * h1 + h2;
        h2 = h1; h1 = hnext;
        k2 = k1; k1 = knext;

        if (a == 2 * a0 && k1 <= Q) {
            // Completed one period; if still within bound, continue
            // (the CF is periodic, it will repeat)
        }
    }
}

int main() {
    const ll Q = 1000000000000LL; // 10^12
    const int N = 100000;
    ll total = 0;
    for (int d = 2; d <= N; d++) {
        ll sq = isqrt(d);
        if (sq * sq == d) continue;
        total += best_denominator(d, Q);
    }
    cout << total << endl;
    return 0;
}