Count-Min Sketch
The probabilistic frequency tracker
Prerequisites
graph TD Bloom[Bloom Filters] --> CMS[Count-Min Sketch] Stream[Streaming / heavy hitters] --> CMS Hash[Hash functions] --> CMS
When to use
- High-volume event streams where you need approximate per-key counts (CDN requests, DDoS IPs, ad impressions) without storing every key.
- Top-K / heavy hitter pipelines that only need frequency estimates bounded by .
- Memory-bounded analytics when exact hash maps would blow the heap but a fixed-size matrix is acceptable.
When not to use
- You need exact counts or must detect a count of zero with certainty (use a hash map or a Bloom filter for membership).
- The stream is tiny or keys are few — a
Counteris simpler and exact. - You cannot tolerate one-sided error (CMS never underestimates; collisions only inflate estimates).
Lab
On the interactive Count-Min Sketch lab, scrub the Stream position slider to replay how the matrix fills, tune Error margin ε to resize width and depth, and query fruit keys to compare the CMS estimate against ground truth from the same stream prefix. Try the Collision hunt preset after predicting whether the sketch will overestimate or match.
Simple Fundamental Explanation
Imagine you manage a massive highway toll booth, and you want to know which car license plates pass through the most frequently.
You can’t afford to keep a giant database of every single license plate and its count—it would require too much memory and be too slow to update.
Instead, you set up a system of several small whiteboards. Every time a car passes, you have several different toll workers look at the license plate. Each worker uses their own secret rule (a hash function) to pick one square on their respective whiteboard, and they add a tally mark to that square.
Because the whiteboards are small, multiple different license plates will inevitably end up adding tally marks to the same square. This means the tally count in any given square might be artificially inflated by other cars.
When you want to know how many times “ABC-1234” passed through, you ask all the toll workers to check the square associated with “ABC-1234” on their whiteboards. They shout out their numbers: “10!”, “15!”, “12!”.
You know that the true count can never be lower than the actual number of times the car passed. Therefore, the lowest number shouted (the minimum) has the least amount of “noise” from other cars. You take the minimum count as your estimate.
This is the Count-Min Sketch. It tracks the frequency of events in a massive stream of data using very little memory. It overestimates sometimes, but it never underestimates.
Deep Dive: How It Works Under the Hood
A Count-Min Sketch is a 2D array (a matrix) of counters, with rows and columns. Every row has its own distinct hash function.
Updating the Frequency (Insertion)
When an item with a count of (usually 1) arrives in the data stream:
- For each row (from 1 to ), calculate the hash: .
- The hash maps to a column index (between 1 and ).
- Add to the cell at row , column :
Matrix[i][j] += c.
Querying the Frequency
When you want to estimate the frequency of item :
- For each row , calculate the same hash to find the column : .
- Retrieve the count from every corresponding cell:
Matrix[i][j]. - Return the minimum of these retrieved values.
Visual Diagram
Initialization (d=3 rows, w=5 columns):
Row 1 (Hash 1): [ 0 | 0 | 0 | 0 | 0 ]
Row 2 (Hash 2): [ 0 | 0 | 0 | 0 | 0 ]
Row 3 (Hash 3): [ 0 | 0 | 0 | 0 | 0 ]
Update item "UserA" (+1):
Hash1("UserA") = 2, Hash2("UserA") = 4, Hash3("UserA") = 1
Row 1: [ 0 | 1 | 0 | 0 | 0 ]
Row 2: [ 0 | 0 | 0 | 1 | 0 ]
Row 3: [ 1 | 0 | 0 | 0 | 0 ]
Update item "UserB" (+1):
Hash1("UserB") = 2, Hash2("UserB") = 1, Hash3("UserB") = 5
Row 1: [ 0 | 2 | 0 | 0 | 0 ] <-- Collision with UserA!
Row 2: [ 1 | 0 | 0 | 1 | 0 ]
Row 3: [ 1 | 0 | 0 | 0 | 1 ]
Query "UserA":
Hash1 -> Row 1, Col 2 = 2
Hash2 -> Row 2, Col 4 = 1
Hash3 -> Row 3, Col 1 = 1
Estimate = MIN(2, 1, 1) = 1. (Accurate, collision in Row 1 bypassed!)
Practical Example: Top-K and DDoS Detection
Content Delivery Networks (CDNs) process billions of requests per second. If an attacker launches a Distributed Denial of Service (DDoS) attack from a specific set of IPs, the CDN needs to identify the most frequent IP addresses instantly to block them.
Using a Count-Min Sketch, the CDN router adds +1 for every IP address it sees. To find the “Heavy Hitters” (the top IPs), the router maintains a small priority queue (a min-heap) alongside the sketch. Every time an IP is inserted, the sketch returns its estimated new count. If the count is higher than the lowest count currently in the Top-K heap, the IP is added to the heap.
This allows real-time DDoS mitigation in a streaming environment using only kilobytes of RAM.
The Mathematics: Equations and In-Depth Analysis
The Count-Min Sketch guarantees bounds on the estimation error. The estimated count of item is related to its true count by:
Where:
- is the total sum of all counts inserted into the sketch.
- is the acceptable error margin (e.g., 0.01 for 1% error).
This means the estimate is never less than the true value, and the overestimate is bounded by a fraction of the total traffic.
Configuring the Sketch Dimensions ( and )
The dimensions of the matrix are chosen based on two desired parameters:
- (Error factor): The acceptable amount of overestimation.
- (Probability of failure): The probability that the estimate exceeds the error bound.
The parameters are calculated as:
Where is Euler’s number ().
Explanation of the Math
- Width (): The expected amount of “noise” added to a single cell in one row by other items is . By setting , Markov’s inequality ensures the error in a single row is bounded with a specific probability.
- Depth (): Because hash functions are independent, the probability that all rows overestimate the value beyond the bound decreases exponentially. Setting ensures the probability of failure drops to the desired .
If you want an error of with a failure probability of (99% confidence): columns. rows. Total memory: bytes (assuming 32-bit integers) KB.
Lab
Count-Min Sketch returns the minimum across hash rows — collisions can only inflate estimates, never deflate them. Same SHA-256 recipe as count_min_sketch.py.
Matrix sketch (3 rows × 272 columns) — showing 64 sampled columns
Before revealing: for "apple" after 360 events, will the CMS estimate overestimate or match the ground truth?
Processed 360 events into a 3×272 matrix. Query "apple": estimate 105, actual 100.
Run implementations
Edit and run Python in the browser (Pyodide), browse the Rust reference in CodeMirror, or open the full sources on GitHub.
Python in the browser (stdlib only).
count_min_sketch.py on GitHub