JA EN
LearnData Structures
·★ MEMBER·PAPER·13 min read

Probabilistic Data Structures — Counting Without Counting

Bloom filters, HyperLogLog and the Count-Min sketch explained from zero — how giving up the right to always be correct buys you memory that never grows, and how large services actually operate these sketches.

ModalitytextTaskalgorithm

Space/Time Trade-offs in Hash Coding with Allowable Errors (Bloom

Primary source — what this article is built on

undefined2026-08-26

Space/Time Trade-offs in Hash Coding with Allowable Errors (Bloom"doi:10.1145/362686.362692
https://dl.acm.org/doi/10.1145/362686.362692"1970)
HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm (Flajolet"doi:10.46298/dmtcs.3545
FusyFusy
Gandouet & MeunierGandouet & Meunier
https://dmtcs.episciences.org/3545"2007)
An Improved Data Stream Summary: The Count-Min Sketch and its Applications (Cormode & Muthukrishnan"doi:10.1016/j.jalgor.2003.12.001
https://doi.org/10.1016/j.jalgor.2003.12.001"2005)

The moment you try to count, you run out of memory

"How many people visited the site today?" The honest answer is easy: put every visitor ID into a set and check its size at the end. At a thousand visitors a day, nothing about this is a problem.

At a hundred million a day, with the same number demanded per hour, per country and per article, everything changes. You now need memory proportional to the number of sets. The same problem shows up in "have I already processed this URL?" and "which IP is hammering us right now?" Answer any of them honestly and you end up remembering everything you have seen.

Probabilistic data structures — usually called sketches — relax that requirement in exactly one place: they allow the answer to be wrong once in a while. What you get in return is memory that does not grow no matter how much data flows through. A HyperLogLog costs 12 KB per aggregate. Twelve kilobytes for a hundred million people, twelve kilobytes for ten billion, with under 1% error.

"Counting exactly" is just another name for "remembering everything"

Why does relaxing one requirement change things by orders of magnitude? The reason sits below the algorithms, in the amount of information involved.

To answer "is it in the set?" for nn distinct keys without ever being wrong, you must retain enough information to distinguish that set from every other possible set — which means keeping something equivalent to the keys themselves. But the moment you say "I will tolerate answering yes for something absent with probability ε\varepsilon," the lower bound on the bits you need drops to this:

bits needednlog21ε\text{bits needed} \gtrsim n \log_2 \frac{1}{\varepsilon}
(1)

Here nn is the number of elements stored and ε\varepsilon is the false-positive rate you accept. In words: log2(1/ε)\log_2(1/\varepsilon) bits per element is enough — about 6.6 bits at 1% error, about 10 bits even at 0.1%. The crucial detail is what is missing from the formula: the length of the key never appears. A 100-byte URL and a 1 KB search query both cost a handful of bits. Tolerating error is a licence to throw away the key itself and keep only a trace of it.

FIG 1Remember-everything schemes ride the n line while a sketch stays pinned to the flat O(1) line. At small volumes the two look alike; at a billion records they differ by orders of magnitude

The question you are asking determines which of three tools you want.

What you want to know Tool Direction of error
Have I seen this before? Bloom filter Wrong only on "yes"
How many distinct things? HyperLogLog A few percent either way
How many times did this occur? Count-Min sketch Only ever overestimates

All three are built on hash functions. A hash erases the structure in your input — sequential IDs, near-identical URLs — and makes anything look like a random bit string, and the statistics ride on top of that "looks random" property. How hash functions themselves are constructed is covered in Hashing and Nearest-Neighbor Search.

What follows takes the three in turn, down to why counting works at all without counting.

Bloom filters: saying "no" with certainty

All you need is an array of mm bits (all zero to start) and kk hash functions. To insert, run the element through the kk hashes and set the kk bits they point at. To query, look at those same kk positions: if even one is zero, the element is definitely absent; if all are one, it is "probably present." The element itself is never stored.

False negatives are impossible by construction — a bit that has been set never goes back. False positives happen when bits set by other elements happen to cover all kk positions. After inserting nn elements, the false-positive rate is:

p(1ekn/m)kp \approx \left(1 - e^{-kn/m}\right)^{k}
(2)

The term 1ekn/m1 - e^{-kn/m} is the probability that a given bit has been set by somebody, and pp is the probability that all kk of them have. Raising kk makes a full house less likely but fills the array faster, so there is an optimum: at k=(m/n)ln2k = (m/n)\ln 2 exactly half the array ends up set. Ten bits per element buys you a false-positive rate just under 1%. The derivation and a compact implementation live in Randomized Algorithms.

Line that up against the bound from earlier and something interesting appears. At ε=1%\varepsilon = 1\% the bound was 6.6 bits per element; a Bloom filter spends 10. In other words it burns roughly 44% more memory than the theoretical optimum. What it buys with that inefficiency is a structure that runs on nothing but bit operations. Cuckoo filters and quotient filters close much of the gap and support deletion, by storing a short fingerprint of each element instead of anonymous bits. A plain Bloom filter cannot delete because clearing a bit would also erase every other element sharing it, manufacturing the false negatives that were supposed to be impossible.

There is also a usage pattern worth internalising: a Bloom filter is not a standalone answer but a gatekeeper placed in front of an expensive check. If it says "no," you skip the expensive work entirely; only when it says "yes" do you consult the real store. A false positive costs one wasted lookup, and correctness is still guaranteed by the store behind it — so the probabilistic error never reaches the user.

The remaining two — HyperLogLog for how many distinct, Count-Min for how many times — replace bit-setting with small statistics. Why does staring at the leading zeros of a hash reveal a crowd size? Why does taking a minimum leave you with overcounting as your only failure mode? That, and how large services actually run these things, is what follows.

Suppose someone tells you they were flipping a coin and saw ten heads in a row. How many flips do you think they made? A run of ten has roughly a 1-in-1024 chance per attempt, so your instinct says about a thousand. Without being told the number of trials, the rarity of the rarest event observed tells you how many ther

What's behind this

§

Members-only from here

371 walkthroughs, 26 textbook chapters, 48 student units and 6 close readings — all included for $4.99/mo, with three new explainers every day. Cancel any time; access runs to the end of the period.

Already a member? Sign in to keep reading

References

  1. Space/Time Trade-offs in Hash Coding with Allowable Errors (Bloom. "doi:10.1145/362686.362692
  2. https://dl.acm.org/doi/10.1145/362686.362692". 1970)
  3. HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm (Flajolet. "doi:10.46298/dmtcs.3545
  4. Fusy. Fusy
  5. Gandouet & Meunier. Gandouet & Meunier
  6. https://dmtcs.episciences.org/3545". 2007)
  7. An Improved Data Stream Summary: The Count-Min Sketch and its Applications (Cormode & Muthukrishnan. "doi:10.1016/j.jalgor.2003.12.001
  8. https://doi.org/10.1016/j.jalgor.2003.12.001". 2005)

This article is written from the source paper above. Where they differ, the original is authoritative.

Comments

Sign in to comment