Skip to content
About
Main content

Count-Min Sketch

What does Count-Min guarantee, and why is it accurate for the head and meaningless for the tail?

A estimates how many times each item appeared in a stream, in fixed memory.

The structure. A table of d rows and w columns of counters. Hash each item once per row (each row has its own ) and increment the one counter it lands on in every row. To estimate an item's count, take the minimum across its d counters. Collisions with other items can only add to a counter, so the minimum is the tightest available overestimate.

class CountMin:
    def __init__(self, w, d):
        self.w, self.d = w, d
        self.t = [[0] * w for _ in range(d)]
    def add(self, x, c=1):
        for r in range(self.d):
            self.t[r][hash((r, x)) % self.w] += c
    def estimate(self, x):
        return min(self.t[r][hash((r, x)) % self.w] for r in range(self.d))

The guarantee, stated precisely, because the imprecise version causes real damage. Choose w = ⌈e/ε⌉ and d = ⌈ln(1/δ)⌉. Then the estimate exceeds the true count by at most ε · N with probability 1 − δ, where N is the total number of events in the stream (the L1 norm of the frequency vector), not the item's own frequency.

error as a fraction of N
chance the bound is exceeded
the total number of events in the stream
counters per row and rows
Data table
Counters needed by error fraction (δ = 1%), whatever the stream size
Error fraction εCounters (w × d)
01.36M
01.21M
01.08M
0962K
0858K
0764K
0681K
0607K
0541K
0482K
0430K
0383K
0341K
0304K
0271K
0242K
0215K
0192K
0171K
0153K
0136K
0121K
0108K
096.2K
085.8K
076.4K
068.1K
060.7K
054.1K
048.2K
043K
038.3K
034.1K
030.4K
0.00127.1K
0.00124.2K
0.00121.5K
0.00119.2K
0.00117.1K
0.00115.3K
0.00113.6K
0.00112.1K
0.00110.8K
0.0019.63K
0.0028.58K
0.0027.64K
0.0026.82K
0.0026.08K
0.0035.42K
0.0034.83K
0.0034.3K
0.0043.83K
0.0043.42K
0.0043.04K
0.0052.71K
0.0062.42K
0.0062.15K
0.0071.92K
0.0081.72K
0.0091.52K
0.011.36K
0.0111.22K
0.0131.08K
0.014965
0.016860
0.018765
0.02685
0.022610
0.025545
0.028485
0.032430
0.036385
0.04345
0.045305
0.05275
0.056245
0.063220
0.071195
0.079175
0.089155
0.1140

Count-Min is a heavy-hitter structure that happens to accept point queries. It is not a frequency oracle. Anyone using it to answer "how many times did this specific rare key appear" is reading random numbers with confidence.

Memory for this example. ε = 0.001 gives w = ⌈2.718 / 0.001⌉ = 2,719. With δ = 1% (so d = ⌈ln 100⌉ = 5) the table has 5 × 2,719 ≈ 13,600 counters, about 54 KB with 4-byte counters, whatever the stream size.

It is a linear sketch, and linearity buys mergeability. The is a fixed random projection matrix applied to the frequency vector. So sketch(A) + sketch(B) = sketch(A ⊎ B) exactly: add the two tables cell by cell and you have the sketch of the combined stream. This is the general rule for the whole article and it is worth stating as a principle, which we call the :

, the signed variant (each item adds +1 or −1 per row, chosen by a second hash), extends this to subtraction, and therefore to deletions and to estimates of the second moment.

Count-Min is not idempotent. Duplicate delivery inflates it directly.

Count-Mean-Min deserves one line: subtract each row's expected noise (N/w) before taking the median, rather than the minimum. On skewed data this substantially reduces the bias that the minimum estimator carries.