100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

What is Reservoir Sampling?

Learn how reservoir sampling picks a uniform random sample from an unbounded stream in O(k) memory, with a full interview answer.

hardQ164 of 227 in Data Structures & Algorithms Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

Reservoir sampling is a randomized algorithm that selects a uniformly random sample of k items from a stream of unknown or unbounded length, using only O(k) memory and a single pass over the data.

The algorithm fills a 'reservoir' array with the first k items directly, then for every subsequent item at index i (0-indexed, i >= k) it generates a random integer j between 0 and i inclusive; if j is less than k, the item replaces the reservoir's j-th slot, otherwise it is discarded. This decreasing-probability replacement rule guarantees every item seen so far has exactly a k/n chance of being in the final reservoir once n items have streamed through, regardless of how large n grows or whether n is known in advance. It is essential when data arrives as an unbounded stream โ€” server logs, live sensor feeds, or paginated APIs โ€” where storing the whole dataset to sample from is impossible. The classic k=1 case reduces to picking one item uniformly at random from a stream with no prior knowledge of its length.

  • O(k) memory regardless of stream length
  • Single pass, no need to know total count upfront
  • Provably uniform sampling probability for every item
  • Works on live, unbounded, or paginated data streams

AI Mentor Explanation

Reservoir sampling is like a scout who must pick exactly 3 standout players from an unknown-length stream of trial candidates walking through the nets, without knowing how many will show up all day. The scout keeps the first 3 candidates automatically, then for every new candidate rolls a random number up to the count seen so far; if that number lands inside the reservoir's range, the new candidate swaps in for a randomly chosen one of the current 3. By closing time, no matter whether 50 or 5,000 candidates walked through, each one had an exactly equal chance of being among the final 3 picks. This is why the scout never needs to know the day's total headcount in advance to guarantee fairness.

Step-by-Step Explanation

  1. Step 1

    Fill the reservoir

    Copy the first k items from the stream directly into a reservoir array of size k.

  2. Step 2

    Process each subsequent item

    For item at 0-indexed position i (i >= k), generate a random integer j in [0, i].

  3. Step 3

    Conditionally replace

    If j < k, overwrite reservoir[j] with the current item; otherwise discard the item.

  4. Step 4

    Repeat until the stream ends

    The final reservoir holds k items, each with exactly k/n probability of inclusion once n total items have streamed by.

What Interviewer Expects

  • Explain why the algorithm works without knowing the stream length in advance
  • State O(k) space and O(n) time for a single pass over n items
  • Walk through the probability argument for why each item has k/n chance of inclusion
  • Name real use cases: sampling live logs, unbounded streams, paginated APIs

Common Mistakes

  • Assuming the stream length must be known beforehand
  • Forgetting to seed the random replacement with the running index i, not a fixed range
  • Confusing reservoir sampling with simple random shuffling of a known-size array
  • Not recognizing k=1 as the simplest special case

Best Answer (HR Friendly)

โ€œReservoir sampling lets me pick a fair random sample from a stream of data even when I have no idea how much data is coming, using only a small fixed amount of memory. I keep the first few items, then for every new item that arrives I give it a shrinking chance of swapping into my sample, which mathematically guarantees every item ends up equally likely to be chosen.โ€

Code Example

Reservoir sampling of k items from a stream
import random

def reservoir_sample(stream, k):
    reservoir = []
    for i, item in enumerate(stream):
        if i < k:
            reservoir.append(item)
        else:
            j = random.randint(0, i)
            if j < k:
                reservoir[j] = item
    return reservoir

# Works even if 'stream' is an unbounded generator
def line_stream(path):
    with open(path) as f:
        for line in f:
            yield line.rstrip("\n")

sample = reservoir_sample(line_stream("huge_log.txt"), k=5)

Follow-up Questions

  • How would you prove each item ends up with probability k/n of being selected?
  • How would you adapt reservoir sampling for weighted sampling?
  • How would you parallelize reservoir sampling across multiple worker shards?
  • What happens to the algorithm if k=1?

MCQ Practice

1. What is reservoir sampling's space complexity for sampling k items from a stream of n items?

Only the reservoir of size k is kept in memory at any time, independent of the total stream length n.

2. Why is reservoir sampling useful for streaming data?

The algorithm processes items one at a time and never needs to know n in advance, making it ideal for unbounded streams.

3. When processing the item at 0-indexed position i (i >= k), what determines if it replaces a reservoir slot?

The decreasing-probability replacement rule (j < k out of i+1 possibilities) is what guarantees uniform final probability.

Flash Cards

What problem does reservoir sampling solve? โ€” Uniformly sampling k items from a stream of unknown or unbounded length.

What is the space complexity of reservoir sampling? โ€” O(k), independent of the total stream length.

How is a new item at position i (i >= k) handled? โ€” A random j in [0, i] is drawn; if j < k, the item replaces reservoir[j].

What is the probability any single item ends up in the final reservoir? โ€” Exactly k/n, once n total items have streamed through.

1 / 4

Continue Learning