-
-
Notifications
You must be signed in to change notification settings - Fork 47k
Bloom Filter #8615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bloom Filter #8615
Changes from 20 commits
173ab0e
08bc970
0448109
486dcbc
4111807
e6ce098
e4d39db
7629686
3926167
280ffa0
5d460aa
cc54095
78d19fd
8b1bec0
28e6691
2fd7196
314237d
9b01472
c132d50
313c80c
18e0dde
54041ff
483a2a0
174ce08
00cc60e
35fa5f5
5cd20ea
7617143
799171a
1a71f4c
4e0263f
e746746
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,107 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||
See https://en.wikipedia.org/wiki/Bloom_filter | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
The use of this data structure is to test membership in a set. | ||||||||||||||||||||||||||||||||||||||||||||||||
Compared to Python's built-in set() it is more space-efficient. | ||||||||||||||||||||||||||||||||||||||||||||||||
In the following example, only 8 bits of memory will be used: | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom = Bloom(size=8) | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Titanic" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
False | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
Initially the filter contains all zeros: | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.bitstring | ||||||||||||||||||||||||||||||||||||||||||||||||
'00000000' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
When an element is added, two bits are set to 1 | ||||||||||||||||||||||||||||||||||||||||||||||||
since there are 2 hash functions in this implementation: | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.add("Titanic") | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.bitstring | ||||||||||||||||||||||||||||||||||||||||||||||||
'01100000' | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Titanic" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
True | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
However, sometimes only one bit is added | ||||||||||||||||||||||||||||||||||||||||||||||||
because both hash functions return the same value | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.add("Avatar") | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("Avatar") | ||||||||||||||||||||||||||||||||||||||||||||||||
'00000100' | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.bitstring | ||||||||||||||||||||||||||||||||||||||||||||||||
'01100100' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
Not added elements should return False ... | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "The Goodfather" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
False | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("The Goodfather") | ||||||||||||||||||||||||||||||||||||||||||||||||
'00011000' | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Interstellar" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
False | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("Interstellar") | ||||||||||||||||||||||||||||||||||||||||||||||||
'00000011' | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Parasite" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
False | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("Parasite") | ||||||||||||||||||||||||||||||||||||||||||||||||
'00010010' | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Pulp Fiction" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
False | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("Pulp Fiction") | ||||||||||||||||||||||||||||||||||||||||||||||||
'10000100' | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
but sometimes there are false positives: | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> "Ratatouille" in bloom | ||||||||||||||||||||||||||||||||||||||||||||||||
True | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.format_hash("Ratatouille") | ||||||||||||||||||||||||||||||||||||||||||||||||
'01100000' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
The probability increases with the number of added elements | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.estimated_error_rate() | ||||||||||||||||||||||||||||||||||||||||||||||||
0.140625 | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.add("The Goodfather") | ||||||||||||||||||||||||||||||||||||||||||||||||
isidroas marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.estimated_error_rate() | ||||||||||||||||||||||||||||||||||||||||||||||||
0.390625 | ||||||||||||||||||||||||||||||||||||||||||||||||
>>> bloom.bitstring | ||||||||||||||||||||||||||||||||||||||||||||||||
'01111100' | ||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||
from hashlib import md5, sha256 | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
HASH_FUNCTIONS = (sha256, md5) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
class Bloom: | ||||||||||||||||||||||||||||||||||||||||||||||||
def __init__(self, size: int = 8) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||
self.bitarray = 0b0 | ||||||||||||||||||||||||||||||||||||||||||||||||
self.size = size | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def add(self, value: str) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||
h = self.hash_(value) | ||||||||||||||||||||||||||||||||||||||||||||||||
self.bitarray |= h | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def exists(self, value: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||
h = self.hash_(value) | ||||||||||||||||||||||||||||||||||||||||||||||||
return (h & self.bitarray) == h | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def __contains__(self, other: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||
return self.exists(other) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def format_bin(self, bitarray: int) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||
res = bin(bitarray)[2:] | ||||||||||||||||||||||||||||||||||||||||||||||||
return res.zfill(self.size) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
@property | ||||||||||||||||||||||||||||||||||||||||||||||||
def bitstring(self) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||
return self.format_bin(self.bitarray) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def hash_(self, value: str) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
res = 0b0 | ||||||||||||||||||||||||||||||||||||||||||||||||
for func in HASH_FUNCTIONS: | ||||||||||||||||||||||||||||||||||||||||||||||||
b = func(value.encode()).digest() | ||||||||||||||||||||||||||||||||||||||||||||||||
position = int.from_bytes(b, "little") % self.size | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
res |= 2**position | ||||||||||||||||||||||||||||||||||||||||||||||||
return res | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def format_hash(self, value: str) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||
return self.format_bin(self.hash_(value)) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def estimated_error_rate(self) -> float: | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
n_ones = bin(self.bitarray).count("1") | ||||||||||||||||||||||||||||||||||||||||||||||||
k = len(HASH_FUNCTIONS) | ||||||||||||||||||||||||||||||||||||||||||||||||
return (n_ones / self.size) ** k | ||||||||||||||||||||||||||||||||||||||||||||||||
cclauss marked this conversation as resolved.
Show resolved
Hide resolved
|
Uh oh!
There was an error while loading. Please reload this page.