Data-Stream Algorithms
Most of this course assumes data sits in fast memory, addressable at will. External sorting relaxed that to a re-readable disk.
╌╌╌╌
Almost every algorithm in this course so far has assumed the same machine: the input sits in memory, and any element is as cheap to touch as any other. That is the RAM model, and it underwrites our analyses of hash tables, balanced trees, and the rest. External sorting was our first crack in that assumption: the data was too big for RAM, so we counted block transfers to a disk we could re-read at will. This lesson drops even that assumption. In the streaming model, the input flows past once, item by item, faster and larger than we can store. We may keep only a tiny working set, and once an item slides by we may never see it again. The question is what we can still compute.
Three models, three currencies
It helps to put the models side by side. Each makes a different assumption about where the data lives and how often we may touch it, and each counts a different cost.
With so little memory we cannot, in general, answer exactly. The obstruction is
information-theoretic. An algorithm with bits of memory has at most
distinguishable states, so if two different prefixes of the stream drive it into
the same state, its future behavior on any common suffix is identical. For a
question like were all items distinct?
there are more prefixes demanding
different future behavior than a sublinear memory has states: two different
-element prefixes must be distinguished, because an adversary can
extend each with an element that appears in one but not the other. Counting those
prefixes forces bits for an exact answer, and a similar argument
yields the same bound for the exact median.
So streaming algorithms return approximate or probabilistic answers, accepting a small, tunable error in exchange for space that is exponentially smaller than the stream. This is the same trade a Bloom filter makes for set membership, and, as there, the tools are hashing and randomized analysis.1
The rest of the lesson is five techniques, each a small variation on that trade-off: sample the stream, count it, fingerprint its frequencies, find its heavy hitters, and estimate how many distinct things it contained.
Reservoir sampling: a uniform sample of unknown length
Problem. A stream of items flows past, with unknown until the end. Keep a uniform random sample of of them, using only space, so that at every moment the stored items are a uniform random -subset of everything seen so far.
The naive approach, store everything and sample at the end, needs space and a known . Reservoir sampling removes both costs with one idea: keep a reservoir of the first items, then for each later item decide on the spot whether it displaces one of them.
Idea. When the -th item arrives (), admit it into the reservoir with probability ; if admitted, it evicts a uniformly random current occupant. In a uniform size- sample of items, each item appears with probability exactly , and that is the probability we give it.
- 1first items of the streamfill the reservoir
- 2
- 3for each remaining item do
- 4is the -th item overall
- 5uniform random integer in
- 6if thenhappens with probability
- 7evict occupant , admit
- 8return
One subtlety in the pseudocode: a single random draw decides both questions at once. The event has probability exactly , which settles admission, and conditioned on admission is uniform over , which settles who gets evicted. One random number per item, work per item, space total.
A worked trace. Run the algorithm with on the stream . The first two items fill the reservoir, so after it holds . Then the coin flips begin.
- Item (). Draw ; admission needs , which happens with probability . Suppose : is admitted and evicts the occupant of slot . Reservoir: .
- Item (). Admission probability . Suppose : the draw exceeds , so is rejected and discarded forever. Reservoir unchanged: .
- Item (). Admission probability . Suppose : is admitted and evicts slot . Reservoir: .
The run above is one sample path; the theorem below says that averaging over all coin flips, every one of the pairs is equally likely, and each individual item ends up retained with probability exactly . This local rule produces a globally uniform sample at every prefix, not just at the end: stop the stream after any , and the reservoir is a uniform -subset of the first items.
Check the theorem against the trace: item arrived at , so it should be retained with probability , admission times two survivals. Item , present from the start, must survive the arrivals at , each of which evicts it with probability : as well. Every path through the coin flips is different; the retention probability is not.
Edge cases and variants. If the stream ends with items, the reservoir simply holds all of them, which is the only correct answer. The special case is worth memorizing on its own: keep one item, and replace it with the -th arrival with probability . That is the entire solution to Linked List Random Node, a uniform pick from a list of unknown length in one pass and space. For very long streams where the random-number generator is the bottleneck, the admission probability shrinks, so most draws are rejections; one can instead draw, in time, the number of items to skip before the next admission, and fast-forward. And when items carry weights and the sample should favor heavy items, a weighted variant assigns each item the key for a uniform and keeps the largest keys in a small heap; unweighted reservoir sampling is the special case where all weights are . (The LeetCode problem Random Pick with Weight is the offline cousin: with all weights in memory, prefix sums and a binary search do the job.)
Reservoir sampling underlies A/B test logging, random log extraction, and fair sampling anywhere the choice is from a sequence whose length is learned only at the end.
Morris counting: a count in bits
Problem. Count events up to , but spend far fewer than the bits an exact counter needs.
Idea. Don't store the count ; store an exponent that approximates . On each increment, advance only probabilistically, with probability , so that grows by one roughly every time the true count doubles. The estimate read out is .
- 1
- 2procedure
- 3with probability doone biased coin flip
- 4
- 5procedure
- 6returnunbiased estimate of the count
Because only needs to reach about , storing takes bits, an exponential saving over the counter it approximates. Counting to a billion takes , which fits in bits.
A worked trace. One possible run of ten increments:
| increment | coin | after | ||
|---|---|---|---|---|
| 1 | up | |||
| 2 | up | |||
| 3 | stay | |||
| 4 | stay | |||
| 5 | up | |||
| 6–10 | stay |
After ten increments the estimate reads against a true count of : off by , which is typical, since a single Morris counter has constant relative error. The estimate is coarse but never drifts systematically, and that is the precise content of the next claim.
Variance, and how averaging fixes it. The same conditioning computes the second moment. With ,
so , and summing from gives . Therefore
The standard deviation is about , comparable to the count itself, so one counter is only good to a constant factor. Chebyshev's inequality makes the repair quantitative: averaging independent counters divides the variance by , so
which drops below once . The total space is bits, still exponentially below an exact counter for fixed accuracy.2 Alternatively, replacing base by makes advance more often and shrinks the per-counter variance at the cost of a slightly larger register.
Morris counting anticipates the sketches that follow: a single small random variable standing in for a quantity too large to store exactly, made accurate by averaging independent copies.3
Where sampling and counting run
Reservoir sampling and Morris counting are old ideas, 1985 and 1978 respectively, that never went away, because the problems they solve, sampling a stream whose length you learn only at the end and counting without room for a counter, keep recurring at scale.
Reservoir sampling (Vitter, 1985, who named it and gave the skip-ahead optimization) is the standard way to draw a fair sample from a log you cannot buffer: distributed systems use it to keep a bounded, uniform sample of requests for tracing and A/B analysis, and Apache Kafka and Spark ship variants. The weighted generalization, keying each item by and keeping the top keys, is the A-Res algorithm of Efraimidis and Spirakis (2006), used when items should be sampled with unequal probability, such as sampling clicks in proportion to dwell time.
Morris counting (Morris, 1978, analyzed rigorously by Flajolet in 1985) foreshadowed a whole family of probabilistic counters. Its modern descendants are the approximate counters inside monitoring systems that must track billions of events per second in a fixed register budget, where the constant relative error is a fair price for shrinking a -bit counter to bits. The averaging trick that repairs its variance, run independent copies and average, is the same move that turns the noisy sketches of the next lesson into trustworthy estimators.4
Continuing on to sketches
Reservoir sampling and Morris counting each summarized the stream as a whole, a representative subset, a single approximate total. The harder and more common questions are about the stream's contents: how often did a particular item appear, which items are frequent, how many distinct items were there? Answering those in sublinear space calls for sketches, small hashed counter arrays with the same exactness-for-space trade-off pushed one level further. This continues in Streaming Sketches.
Takeaways
- The streaming model processes in one pass, keeping only state; items are seen once and discarded. An information-theoretic argument forces exact answers to cost space, so the model trades exactness for approximate, probabilistic answers.
- The three cost models line up by what they assume about the data: RAM (fits, freely re-readable, spends time, exact), external memory (re-readable but too big, spends block transfers, exact), streaming (seen once, spends space, approximate).
- Reservoir sampling keeps a uniform -sample of an unknown-length stream in space by admitting item with probability ; the telescoping product makes every item equally likely to be retained, at every prefix. The case is a uniform pick from a list of unknown length in space.
- Morris counting approximates a count up to in bits with the unbiased estimator ; its variance is cut by averaging independent copies, the same technique the sketches of the next lesson use.
- These two summarize the stream as a whole; estimating its per-item frequencies needs the sketches that follow.
Footnotes
- Skiena, §3.7 and the randomized-sampling discussion: hashing as the engine of streaming sketches, and uniform sampling without replacement from a sequence. ↩
- CLRS, App. C — Counting and Probability: the expectation, Markov-inequality, and union-bound arguments behind the reservoir and Morris-counter guarantees. ↩
- Erickson, Ch. — Randomized Algorithms: analysis of estimators by their expectation and variance, and the averaging trick that drives accuracy with independent copies. ↩
- Vitter,
Random sampling with a reservoir
(1985); Efraimidis & Spirakis,Weighted random sampling with a reservoir
(A-Res, 2006); Morris,Counting large numbers of events in small registers
(1978), analyzed by Flajolet,Approximate counting: a detailed analysis
(1985). ↩
╌╌ END ╌╌