Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 3 – Hashing: maps, sets and hash functions

Bloom filters and count-min sketches

How a Bloom filter answers "definitely not" or "probably yes" in a few bits per key, how a count-min sketch estimates counts, and the errors they allow.

  • Advanced
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free core, Pro depth: 12 of 30 minutes free
Free: How a Bloom filter works · How a count-min sketch works · The formulas, and one worked number · 2 more sections. With a pass: 2 in-depth parts and 3 interview answers, about 18 minutes.

What you will learn

  • Implement a Bloom filter and estimate its false-positive rate
  • Explain why a Bloom filter never gives false negatives
  • Estimate frequencies in a stream with a count-min sketch
  • Size both structures from a target error and the number of items

Before you start

What you’ll learn · Before you start

What you will learn

  • Implement a Bloom filter and estimate its false-positive rate
  • Explain why a Bloom filter never gives false negatives
  • Estimate frequencies in a stream with a count-min sketch
  • Size both structures from a target error and the number of items

Before you start

On this page

Bloom filters answer “is this key in the set?” using a few bits per key, with an error that only goes one way: the answer is either “definitely absent” or “probably present”. Count-min sketches answer “how often has this item appeared?” from a small, fixed table of counters, and their estimates can be too high but never too low. Both give up exact answers to save memory, and both let you choose the error in advance: a Bloom filter with 10 bits per key is wrong for about 1 % of absent keys, whatever the keys look like. They are used where an exact set or exact counts would not fit in memory, or would cost a slow lookup: skipping disk reads for keys that are not there, or tracking the most frequent search terms in a stream too large to store.

How a Bloom filter works

A Bloom filter, described by Burton Bloom in 1970, is an array of m bits, all 0 at the start, and k hash functions. Adding a key sets the k bits that its hashes point to. Querying a key checks the same k bits: if any of them is 0, the key was never added; if all are 1, it probably was, because those bits may also have been set by other keys.

A Bloom filter: adding a key sets k bits; a query checks the same k bits, answering definitely absent or probably present.add(key): hash it k timesand set those k bits16 bits after asha, ravi, meera0010010101101000might_contain(key): hash itthe same k waysAre all k bits set?No: definitely absent(no false negatives)Yes: probably present(may be a false positive)noyes

Adding and querying a Bloom filter

Text description of the diagram

The diagram is a flow from top to bottom.

  1. add(key) hashes the key k times and sets those k bits of the bit array.
  2. The example array of 16 bits, after adding asha, ravi and meera with k = 2, reads 0010010101101000.
  3. might_contain(key) hashes the key in the same k ways.
  4. A decision: are all k of those bits set?
  5. If not, the answer is "definitely absent": an added key always finds all its bits set, so there are no false negatives.
  6. If they are, the answer is "probably present": the bits may have been set by other keys, so this answer can be a false positive.
16 bits, 2 hash functions, 3 names Python · small/tiny_bloom.py
"""A Bloom filter of 16 bits with 2 hash functions, small enough to print every bit.

The two bit positions of a name come from 64-bit FNV-1a hashes of the name, combined as h1 + i * h2 (mod 16).
"""
M, K = 16, 2


def fnv1a_64(text, seed=0xCBF29CE484222325):
    h = seed
    for byte in text.encode("utf-8"):
        h ^= byte
        h = (h * 0x100000001B3) & 0xFFFFFFFFFFFFFFFF
    return h


def positions(name):
    h1, h2 = fnv1a_64(name), fnv1a_64(name, seed=0x84222325CBF29CE4) | 1
    return [(h1 + i * h2) % M for i in range(K)]


bits = [0] * M
for name in ("asha", "ravi", "meera"):
    for p in positions(name):
        bits[p] = 1
    print(f"add {name!r:8} -> bits {positions(name)}")
print("bit array:", "".join(map(str, bits)), f"({sum(bits)} of {M} bits set)")

for name in ("ravi", "john", "kiran", "farah", "neel", "tara"):
    ps = positions(name)
    if all(bits[p] for p in ps):
        verdict = "probably present" + ("" if name in ("asha", "ravi", "meera") else " (a false positive)")
    else:
        verdict = "definitely absent"
    print(f"query {name!r:8} -> bits {ps}: {verdict}")

Output

add 'asha'   -> bits [10, 9]
add 'ravi'   -> bits [5, 2]
add 'meera'  -> bits [7, 12]
bit array: 0010010101101000 (6 of 16 bits set)
query 'ravi'   -> bits [5, 2]: probably present
query 'john'   -> bits [12, 13]: definitely absent
query 'kiran'  -> bits [10, 7]: probably present (a false positive)
query 'farah'  -> bits [15, 12]: definitely absent
query 'neel'   -> bits [5, 6]: definitely absent
query 'tara'   -> bits [7, 14]: definitely absent

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 tiny_bloom.py

After three names, 6 of the 16 bits were set. “john” hit bit 13, which is 0, so it is certainly absent. “kiran” was never added, yet both of its bits, 10 and 7, had been set by “asha” and “meera”: a false positive. False negatives are impossible, because adding a key sets all of its bits and nothing in a plain Bloom filter ever clears a bit. That is also why a plain filter cannot delete: clearing a key’s bits could clear bits that other keys share.

Two variants come up again below. A counting Bloom filter keeps a small counter instead of a bit in each cell, so that keys can be removed. Double hashing builds all k positions from two hashes, as h1 + i·h2, which Kirsch and Mitzenmacher showed keeps the same false-positive rate in the limit.

How a count-min sketch works

A count-min sketch, introduced by Cormode and Muthukrishnan in 2005, is a table of d rows of w counters, with one hash function per row. Adding an item adds 1 to one counter in every row, the one its row’s hash picks. The estimate for an item is the smallest of its d counters.

2 rows of 8 counters, a stream of 12 searches Python · small/tiny_cms.py
"""A count-min sketch of 2 rows by 8 counters, counting a short stream of search terms.

Each row hashes a term to one counter and adds 1 to it; the estimate of a term is the smallest of its counters, so it
can only be too high, never too low.
"""
from collections import Counter

D, W = 2, 8


def fnv1a_64(text, seed):
    h = seed
    for byte in text.encode("utf-8"):
        h ^= byte
        h = (h * 0x100000001B3) & 0xFFFFFFFFFFFFFFFF
    return h


SEEDS = (0xCBF29CE484222325, 0x84222325CBF29CE4)
table = [[0] * W for _ in range(D)]


def add(term):
    for row in range(D):
        table[row][fnv1a_64(term, SEEDS[row]) % W] += 1


def estimate(term):
    return min(table[row][fnv1a_64(term, SEEDS[row]) % W] for row in range(D))


stream = ["cricket", "rain", "cricket", "train", "rain", "cricket", "exam", "cricket", "gold", "rain", "train", "cricket"]
for term in stream:
    add(term)
for row in range(D):
    print(f"row {row}: {table[row]}")
exact = Counter(stream)
for term in sorted(exact, key=lambda t: (-exact[t], t)):
    print(f"{term:>7}: exact {exact[term]}, estimate {estimate(term)}")
print(f"never seen 'chess': estimate {estimate('chess')}")

Output

row 0: [5, 2, 1, 4, 0, 0, 0, 0]
row 1: [0, 0, 4, 5, 0, 0, 2, 1]
cricket: exact 5, estimate 5
   rain: exact 3, estimate 4
  train: exact 2, estimate 2
   exam: exact 1, estimate 1
   gold: exact 1, estimate 4
never seen 'chess': estimate 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 tiny_cms.py

Every counter an item touches includes all of that item’s own arrivals, so no estimate can be below the true count. Other items that land in the same counter only add to it, which is why “rain” was estimated at 4 instead of 3 and “gold” at 4 instead of 1. Taking the smallest counter keeps the error down, because an item would need to collide in every row to be badly overestimated. Count-min sketches estimate counts; they cannot list the items they have seen, because the table keeps no keys.

The formulas, and one worked number

For a Bloom filter with m bits, k hash functions and n keys, the chance that a key never added looks present is about

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

and it is smallest when k is about (m / n) ln 2. With 10 bits per key, the best k is 7 and p is about 0.82 %, so a filter for a million keys needs about 1.2 MB, against the tens of megabytes an exact set of the same keys would take.

For a count-min sketch over a stream of N items, choosing w = ⌈e / ε⌉ and d = ⌈ln(1 / δ)⌉ means that no estimate falls below the true count, and that each one exceeds it by more than εN with probability at most δ. With ε = 0.001 and δ = 0.01, that is 5 rows of 2,719 counters, whatever the number of distinct items.

Bloom filter, count-min sketch or a hash set?

Three ways to remember what a stream contained
Criterion Hash set or hash mapBloom filterCount-min sketch
Question it answers Exactly: is it there, and how oftenIs it there? Definitely not, or probablyAbout how often has it appeared?
Error NoneFalse positives only, at a rate you chooseOverestimates only, bounded by εN with probability 1 − δ
Memory Grows with the keys, tens of bytes eachA few bits per key, about 10 for 1 %Fixed: d × w counters, independent of the keys
Delete or decrement YesNo, unless counters replace bitsDecrements are possible but break the guarantee if counts go below the truth
Lists the keys YesNoNo
When to choose The keys fit in memory and answers must be exactSkipping work for keys that are certainly absentFrequencies and heavy hitters in huge streams

Storage engines show the trade-off well. LevelDB, an open-source storage library, can keep a Bloom filter for each of its files, and its documentation says that with 10 bits per key the filter cuts the unnecessary disk reads of a lookup by a factor of about 100. A read for a key that a filter rules out never touches that file.

Check yourself

Check yourself

4 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 4 A Bloom filter checks a key's k bits and finds one of them is 0. What can it conclude?

    Choose one answer.

    Show the answer to question 1

    Answer: The key was certainly never added

    Adding a key sets all k of its bits, and nothing in a plain Bloom filter clears a bit, so a 0 among them proves the key was never added. Only the opposite answer, all bits set, can be wrong.

  2. Question 2 of 4 A Bloom filter has 1,200,000 bits for 100,000 keys. About what number of hash functions minimises its false-positive rate?

    Type a number.

    Show the answer to question 2

    Answer: 8 hash functions (anything from 7.5 to 8.5 counts)

    The best k is about (m / n) ln 2 = 12 × 0.693 ≈ 8.3, so 8. The rate changes little between neighbouring values, so 8 or 9 both do well.

  3. Question 3 of 4 A count-min sketch estimates that a search term appeared 420 times. What do you know about the true count?

    Choose one answer.

    Show the answer to question 3

    Answer: It is at most 420

    Every counter an item touches holds all of its own arrivals plus those of colliding items, so the smallest counter can only overestimate. The true count is 420 or less.

  4. Question 4 of 4 A service must skip a slow database lookup whenever a username has certainly never been registered. Which structure fits?

    Choose one answer.

    Show the answer to question 4

    Answer: A Bloom filter of the registered usernames

    A Bloom filter answers "definitely absent" with certainty, which is exactly when the lookup can be skipped; a "probably present" answer falls back to the database. A count-min sketch estimates counts, and the set of names never registered is unbounded.

Bloom filters measured: false positives, sizing and deletion Pro: opens with a Pro, Premium or Ultimate pass

The formula for false positives is an approximation, so this part measures it on 100,000 keys, turns it into a sizing table you can use directly, and reproduces what goes wrong when a plain filter deletes.

In this part:

  • False positives against the formula, measured for k = 1 to 10
  • Sizing a filter from a target rate, with numbers
  • Why a plain filter cannot delete, reproduced, and the counting fix
  • Practise: build a Bloom filter
  • Build a Bloom filter with k hash functions
    • Results of the sample tests

3 runnable examples (Python) · 1 exercise with sample tests · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Count-min sketches measured: error bounds and heavy hitters Pro: opens with a Pro, Premium or Ultimate pass

A count-min sketch promises a bounded overestimate with a stated probability. This part checks that promise on a skewed stream of 200,000 searches, proves where the bound comes from, and uses the sketch to find the most frequent terms without storing them all.

In this part:

  • The error guarantee, checked on a skewed stream
  • Why the overestimate is bounded: a proof sketch
  • Finding the most frequent terms, with numbers
  • Sizing a sketch for a whole day, with numbers
  • When a count-min sketch is the wrong tool

1 runnable example (Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    Why can a Bloom filter give false positives but never false negatives?

    Adding a key sets all k bits that its hash functions point to, and a plain Bloom filter never clears a bit. So a key that was added always finds all its bits set, and the filter can never answer "absent" for it: no false negatives. A key that was never added can still find all its bits set, because other keys may have set them, and then the filter wrongly answers "probably present": a false positive. The rate of those depends on the number of bits per key and the number of hash functions, about 0.8 % with 10 bits per key and 7 hash functions. Deleting by clearing bits would break the guarantee, since other keys share those bits.

  2. Mid to senior

    A sign-up form must say at once whether a username might already be taken. There are 50 million existing usernames, and at most 1 % of "might be taken" answers may be wrong, each one checked against the database. Size a Bloom filter: bits, hash functions and memory. What happens as new users keep signing up?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    Your service keeps a Bloom filter of active account IDs to skip database lookups for unknown IDs. Accounts can now be deleted, about 1 % of them a month. How do you remove deleted IDs, and what are the trade-offs of each option?

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    You must report the 100 most frequent search terms of a day with 2 billion searches, using a few megabytes of memory per server. Size a count-min sketch, say how far off an estimate can be, and explain how you find the top 100 without storing every term.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Bloom filters store set membership in a few bits per key and answer “definitely absent” or “probably present”.
  • A plain Bloom filter has no false negatives and cannot delete; counting Bloom filters can, at about four times the memory.
  • About 10 bits per key with 7 hash functions gives a false-positive rate near 0.82 %.
  • Count-min sketches estimate frequencies from d rows of w counters; estimates are never too low, and too high by more than εN only with probability δ.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.