Approximate Membership Query: Bloom Filter and Quotient Filter
Category: DS & Algo
Bloom Filter
A Bloom Filter is a kind of hash table that's probabilistic. Its biggest characteristic, compared to a regular hash table, is that it has an advantage in both space and time.
A Bloom Filter can be represented as an array whose values are or . When you query whether an element exists, a Bloom Filter can only give one of two answers: "no" or "maybe". In other words, the only kind of misjudgment possible is a False Positive.
This trait makes it well suited to scenarios that need a "first-pass filter":
- Filtering out unpopular pages that "only ever get clicked once" (a page only gets put into the cache once its access count hits a threshold or a second hit, to save bandwidth
- Chrome used to use a Bloom Filter to filter malicious URLs (ones that might be malicious get sent off to a separate server for further processing)
- Looking up elements that don't exist in a database, to avoid wasting a lot of time
- Filtering out content a user has already seen in a recommendation system
To implement a Bloom Filter, you need to pick a number as the size of the array, and initialize the array to . Then pick hash functions, which map the input to positions in the array. When storing an element, run it through the hash functions and set the values at those positions in the array to . When querying an element, only return True if all positions the hash functions map to are , otherwise return False — this is how it achieves a query that can only ever be a False Positive.

Notes
- and can be decided by 1. the expected number of elements to store and 2. an acceptable false-positive rate
-
The more elements stored and the lower the required false-positive rate, the larger the array length
-
The lower the required false-positive rate, the more hash functions needed
-
The values the hash functions map to should be evenly distributed, with no correlation between different positions — and designing hash functions gets harder as grows
-
Compared to a regular data structure, a Bloom Filter doesn't need to store the element itself (since it only cares about the hash value), giving it an advantage in both storage and lookup (), and it can also satisfy certain confidentiality requirements
-
An element already stored in a Bloom Filter can't be deleted. You could reset the hashed positions back to , but that would affect other elements and lose the guarantee of no False Negatives
-
The false-positive rate rises as more data is added, but for a sufficiently large this becomes negligible
Quotient Filter
A Quotient Filter addresses the Bloom Filter's weakness of not being able to delete elements, and multiple Quotient Filters can be merged together.
A Quotient Filter's operations are similar to a Bloom Filter's — inserting and querying elements. There's only a single hash function, and once an element goes through it, the result gets split into two parts:
- The leading bits (MSB) are the Quotient, used to indicate the element's position in the array
- The remaining bits (LSB) are the Remainder (Fingerprint), which get stored in the array
If we need an array of length , the first three bits of the hash result () become the Quotient, and the remaining bits (the Remainder) get stored in the array.
Besides the Remainder, each slot in the array also uses three bits as metadata:
is_occupied: indicates this slot is some element's "original home" (Canonical Location) — note this isn't necessarily the home of the element actually stored hereis_continuation: means the element in this slot is the head of a Runis_shifted: indicates the element in this slot is no longer at its Canonical Location, and has been shifted further down

When an element's position matches its quotient, that position is called its Canonical Location. When more than one element shares the same Quotient, we say they belong to the same Run.
Say we insert three elements , , in order, with quotients , , respectively. Then:
- gets inserted at slot , with metadata
- sees that slot is occupied, and its metadata is , meaning it's the head of that Run. So it linear-probes into slot , with metadata , belonging to the same Run as
- sees that slot is occupied, and its metadata is , meaning there's an element in this slot that belongs to neither this Run nor is the head of its own Run. So it first sets slot 's
is_occupiedto , marking slot as belonging to its own Run, making it , then linear-probes into slot , settingis_shiftedto , with metadata
So:
- When querying , slot 's metadata is and the remainder matches, so it's easy to tell that's its Canonical Location
- When querying , slot 's remainder doesn't match, so you keep searching forward, and you're guaranteed to find before hitting an
is_continuationof - When querying , you'll first see
is_occupiedis , but the remainder doesn't match andis_continuationis , so you can tell this slot belongs to a different Run. At this point you need another, more involved set of rules to dig further — it's a bit complicated, and nobody online seems to explain it clearly either, so check the original paper if you're curious. Still, as long as you remember that Quotient, Remainder, and metadata — these three things — let you implement an efficient AMQ that also supports deleting elements
Deleting is also fairly complex — it involves finding the element, removing the remainder, shifting things to fill the gap, and updating metadata — but it's guaranteed to be safe to delete.
Finally, imagine an element that was never inserted, but whose remainder happens to be found in just the right spot — that's a Quotient Filter's false positive, coming from a remainder collision.
Summary
When the data volume is huge and you need a first-pass-filter data structure, a Bloom Filter is still the go-to choice, because it's mature, simple, and easy to implement. A Quotient Filter, on the other hand, uses linear probing, which is friendly to hardware caches — its sequential-read speed often beats a Bloom Filter's — and it's well suited to scenarios that need dynamic deletion.
Reference
- Bloom Filters | Algorithms You Should Know #2 | Real-world Examples
- (counting) quotient filter (this one's examples are decent, but there are enough typos that it gets distracting — still, it gives a rough feel for how a quotient filter works)
- Quotient Filter Explained | Probabilistic Data Structure To Check Membership