Function: remove in bloom-filter/bloom_filter.py

Date: 2026-05-29

Time: 10:18

CountingBloomFilter.remove

Purpose

This method removes an item from a counting Bloom filter by decrementing the counters at the item's hash positions. Standard Bloom filters can't support deletion because clearing a bit might affect other items that hash to the same position. Counting Bloom filters solve this by replacing single bits with counters — remove is the operation that makes that tradeoff worthwhile.

Contract

Parameters

| Parameter | Type | Description |

|-----------|------|-------------|

| self | CountingBloomFilter | The filter instance |

| item | str | The item to remove. Must be a string (passed to _hashes, which calls .encode("utf-8")) |

Edge cases: If item was never added but all its hash positions happen to be non-zero due to collisions from other items, the removal will silently succeed — a false positive removal. This is inherent to probabilistic data structures.

Return Value

Returns None. The effect is entirely through mutation.

Algorithm

The method works in two passes over the hash positions:

1. Compute positions: Call hashes(item, self.k, self._m) to get k bucket indices via double-hashing on MD5.

2. Validation pass (lines 3–5): Check every position. If *any* counter is zero, the item definitely wasn't added — raise ValueError. This is done as a separate pass to avoid partial decrements: if position 2 of 5 is zero, you don't want to have already decremented positions 0 and 1.

3. Decrement pass (lines 6–8): For each position, decrement the counter *only if* it hasn't saturated to maxval. A saturated counter is left untouched because the true count could be maxval + n — decrementing would undercount.

4. Update count: Decrement self._count by 1.

The two-pass design is the key detail — it provides atomicity-like behavior within a single-threaded context: either all counters are decremented, or none are.

Side Effects

Error Handling

Usage Patterns


cbf = CountingBloomFilter(expected_items=100, false_positive_rate=0.01)
cbf.add("alice")
cbf.add("bob")

cbf.remove("alice")       # succeeds, decrements counters
assert "alice" not in cbf  # likely true, but not guaranteed (other items may hold those counters up)

cbf.remove("carol")       # raises ValueError — carol was never added (probably)

Callers should be aware that remove on a counting Bloom filter can introduce false negatives — something standard Bloom filters never have. If items A and B collide on a position, removing A might drop that counter to zero, making B appear absent.

Dependencies

Topics to Explore

Beliefs