All Euler problems
Project Euler

Shortest Distance Among Points

A sequence of points is defined using a pseudo-random number generator: s_0 = 290797 s_(n+1) = s_n^2 mod 50515093 P_n = (s_2n, s_(2n+1)) Let d(k) be the shortest Euclidean distance between any two...

Source sync Apr 19, 2026
Problem #0816
Level Level 09
Solved By 2,717
Languages C++, Python
Answer 20.880613018
Length 362 words
modular_arithmeticgeometrysequence

Problem Statement

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

We create an array of points $P_n$ in a two dimensional plane using the following random number generator:

$$\begin{cases} s_0=290797 \\ s_{n+1}={s_n}^2 \bmod 50515093 \end{cases} \textit{ and } \quad P_n = (s_{2n}, s_{2n + 1})$$

Let $d(k)$ be the shortest distance of any two (distinct) points among $P_0, \cdots, P_{k - 1}$.

E.g. $d(14)=546446.466846479$.

Find $d(2000000)$. Give your answer rounded to $9$ places after the decimal point.

Problem 816: Shortest Distance Among Points

Mathematical Analysis

This is the classic Closest Pair of Points problem from computational geometry.

Brute Force Approach

Checking all (n2)\binom{n}{2} pairs has O(n2)O(n^2) complexity, which is infeasible for n=2,000,000n = 2{,}000{,}000.

Divide and Conquer Algorithm

The classic algorithm by Shamos and Hoey achieves O(nlogn)O(n \log n):

  1. Sort points by x-coordinate.
  2. Divide the set into two halves by a vertical line.
  3. Conquer recursively: find the closest pair in each half, let δ=min(dL,dR)\delta = \min(d_L, d_R).
  4. Combine: Check pairs that straddle the dividing line. Only points within distance δ\delta of the dividing line need checking, and for each such point, at most 7 other points in the strip need comparison.

Grid/Hash-Based Approach (Used Here)

An alternative O(n)O(n) expected-time randomized algorithm:

  1. Compute an initial estimate δ\delta from the first few points or a random sample.
  2. Build a grid with cell size δ\delta.
  3. For each new point, check only neighboring cells (at most 9 cells in 2D).
  4. If a closer pair is found, rebuild the grid with the new δ\delta.

For practical purposes with pseudo-random points, a sorted strip-based approach works well:

  1. Sort points by x-coordinate.
  2. Use a set (balanced BST) sorted by y-coordinate for the “active” window.
  3. For each point, remove points whose x-distance exceeds the current best δ\delta.
  4. Query nearby points in the y-sorted set.

This sweep-line approach runs in O(nlogn)O(n \log n).

Editorial

We generate all 2,000,000 points using the recurrence. We then sort by x-coordinate. Finally, sweep from left to right, maintaining a set of active points sorted by y.

Pseudocode

Generate all 2,000,000 points using the recurrence
Sort by x-coordinate
Sweep from left to right, maintaining a set of active points sorted by y
For each point p:
Return delta

Correctness

Theorem. The method described above computes exactly the quantity requested in the problem statement.

Proof. The preceding analysis identifies the admissible objects and derives the formula, recurrence, or exhaustive search carried out by the algorithm. The computation evaluates exactly that specification, so every valid contribution is included once and no invalid contribution is counted. Therefore the returned value is the required answer. \square

Complexity Analysis

  • Time: O(nlogn)O(n \log n) for sorting and sweep line operations.
  • Space: O(n)O(n) for storing points and the active set.

Answer

20.880613018\boxed{20.880613018}

Code

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

C++ project_euler/problem_816/solution.cpp
#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    const int K = 2000000;
    const long long MOD = 50515093LL;

    // Generate sequence s_0, s_1, s_2, ...
    // s_0 = 290797, s_{n+1} = s_n^2 mod 50515093
    // P_n = (s_{2n}, s_{2n+1})
    vector<pair<long long,long long>> pts(K);
    long long s = 290797;
    for(int i = 0; i < K; i++){
        long long x = s;
        s = (s * s) % MOD;
        long long y = s;
        s = (s * s) % MOD;
        pts[i] = {x, y};
    }

    // Sort by x
    sort(pts.begin(), pts.end());

    // Sweep line with set sorted by (y, x)
    auto cmp = [](const pair<long long,long long>& a, const pair<long long,long long>& b){
        if(a.second != b.second) return a.second < b.second;
        return a.first < b.first;
    };
    set<pair<long long,long long>, decltype(cmp)> active(cmp);

    double best = 1e18;
    int left = 0;

    for(int i = 0; i < K; i++){
        long long d = (long long)ceil(best);

        // Remove points too far left
        while(left < i && pts[i].first - pts[left].first > d){
            active.erase(pts[left]);
            left++;
        }

        // Check nearby points in y range
        pair<long long,long long> lo = {-1, pts[i].second - d};
        auto it = active.lower_bound(lo);

        for(; it != active.end() && it->second <= pts[i].second + d; ++it){
            double dx = (double)(pts[i].first - it->first);
            double dy = (double)(pts[i].second - it->second);
            double dist = sqrt(dx*dx + dy*dy);
            if(dist < best){
                best = dist;
            }
        }

        active.insert(pts[i]);
    }

    cout << fixed << setprecision(9) << best << endl;

    return 0;
}