Choose items uniformly at random from a stream of unknown length, using memory and one pass.

Algorithm R — the classic

vector<int> reservoir(istream& in, int k) {
    vector<int> res;
    int x, i = 0;
    while (in >> x) {
        if (i < k) res.push_back(x);
        else {
            int j = uniform_int_distribution<int>(0, i)(rng);
            if (j < k) res[j] = x;
        }
        i++;
    }
    return res;
}

Keep the first ; for the -th item (, 0-indexed), keep it with probability , replacing a uniformly chosen slot.

Why it is uniform

Claim: after processing items, each is in the reservoir with probability .

Induction. True for . Suppose it holds after items. Item enters with probability by construction. An earlier item survives iff either the new item is rejected (probability ), or it is accepted but evicts a different slot (probability ). Total:

Variants

Single item ()

int chosen; int i = 0;
while (in >> x) { if (uniform_int_distribution<int>(0, i)(rng) == 0) chosen = x; i++; }

Keep the -th item with probability . Two lines.

Weighted reservoir sampling (A-Res)

Give item the key with uniform in , and keep the largest keys in a min-heap.

priority_queue<pair<double,int>, vector<pair<double,int>>, greater<>> pq;
double key = pow(uniform_real_distribution<double>(0,1)(rng), 1.0 / w);
if ((int)pq.size() < k) pq.push({key, item});
else if (key > pq.top().first) { pq.pop(); pq.push({key, item}); }

Selects each item with probability proportional to its weight, in one pass and memory.

Algorithm L — skip ahead

Instead of drawing a random number per item, compute how many items to skip before the next acceptance. Reduces the random-number count from to — the standard optimisation for very long streams.

Distributed sampling

Each machine keeps its own reservoir with a count; merge by sampling from the reservoirs weighted by their counts. Uniformity is preserved.

When it applies

SituationWhy reservoir sampling
The input length is unknown in advancecannot pre-allocate or index
The data does not fit in memory space
A single pass is all you getstreaming
Sampling log lines, telemetry, network packetsthe standard production use
Choosing a random element of a linked listone traversal
Random node of a tree during a DFS variant
Randomly selecting from a filtered subsetapply the filter as you stream

If you do know the length

Simply pick random indices, or shuffle and take the first — simpler and faster. Reservoir sampling is specifically for the unknown-length or streaming case.

shuffle(v.begin(), v.end(), rng);
vector<int> sample(v.begin(), v.begin() + k);

Or, for , sample indices with a hash set to avoid the full shuffle: expected.

ProblemAlgorithmSpace
Uniform sample of reservoir sampling
Count distinct elementsHyperLogLog
Frequent items (“heavy hitters”)Misra-Gries, Count-Min sketch
Approximate quantilest-digest, GK summary
Membership testingBloom filter bits
Majority elementBoyer-Moore voting

Boyer-Moore voting is the deterministic cousin worth pairing with this: it finds a strict majority element in one pass and space, with no randomness at all.

See also: Randomized Algorithms · Boyer-Moore Voting · Probability