A visual interactive guide to Bloom filters
samwho.dev
samwho.dev
And if you want to try some code, I've prepared three toy Bloom filter implementations to accompany the article:
JavaScript: https://codapi.org/embed/?engine=browser&sandbox=javascript&...
Python: https://codapi.org/embed/?sandbox=python&src=gist:e7bde93f98...
Go: https://codapi.org/embed/?sandbox=go&src=gist:e7bde93f98c5e4...
But yeah, I too know the sorrow of getting MJ to generate specific text in images - funnily enough, I too wanted a question-mark for my cat image (for https://gwern.net/review/cat thumbnail ) and I just couldn't get MJv5 to do it and had to settle for making the entire cat a question-mark: https://gwern.net/doc/cat/psychology/2023-11-04-gwern-midjou... It looks cool and I'm satisfied with it, but it wasn't what I had been going after.
In MJ's defense, MJv6 (c. January 2024) is considerably better at text, and you could probably get that question-mark now with some prompting & maybe region-inpainting.
Glad you like them, they were hard to get right :D
1. They are very close to the information theoretic minimum needed to encode the information so are more space-efficient than bloom filters or cuckoo filters.
2. They have a natural way to store metadata (at the cost of additional space of course) so you can associate a few bits of metadata with each set bit at pretty reasonable space cost.
Link for people reading this: https://systemdesign.one/quotient-filter-explained/
I remember there's more obscure newer stuff someone showed me in 2019 tho.
Something I spent time thinking about, but wasn’t able to find a huge amount of information on, is how you could use compression alongside bloom filters. You could make enormous bloom filters that make use of run length encoding or sparse bitmaps. You sacrifice insert and lookup speed but you could make enormous bloom filters this way.
BTW we ended up open sourcing that BF library that encodes and decodes filters in multiple languages, the company has been out of business for nearly a decade but the project is still out there https://github.com/EverythingMe/inbloom
My recollection is a little fuzzy now but as I recall, as we parsed each pagination of the ePub, we would hash or index (the first three characters of?) every word on the page. Each page got its own bitfield or some such structure to hold these hashes.
When a user typed a word to search we could potentially eliminate a large percentage of the pages of the ePub. If a page had no hash matches to the hashed search term then we knew the word was not contained on the page at all.
A best-case example might be searching for "Jabberwocky" in the book "Through the Looking Glass". Very likely all but one page of the ePub will fall away.
As is pointed out in the article, if we did get a hash match for a page it was not a guarantee the word was there — but then we would drop down to a more standard string compare to find not only if the word truly existed on the page but where it was as well (or if it existed in multiple places on the page).
The worst case was of course entering a search term like the word "the" where potentially every page would have to be exhaustively string-compared. But that kind of falls in the lap of the user — why did you search for "the"?
On a side note, I just spent like an hour on your blog. Fascinating stuff!
WebKit, at the time, was not very good at pagination, something ePub requires. iBooks work resulted in a number of "Radars" filed against WebKit with regard to pagination. As a side effect I suspect printing a web page from Safari (who does that?) is markedly better since.
Rather than using multiple hash functions, would it make more sense to use a single algorithm over (prefix | input), with k different prefixes? This may allow computing those hashes in parallel, using SIMD for example, and caching the prehash state of the prefixes.
Edit: looks like there has been some research on this: https://ieeexplore.ieee.org/document/8462781
Paper: https://link.springer.com/chapter/10.1007/11841036_42
Have loved bloom filters for a long time. If probabilistic data structures that make use of hashing are your thing, you should also check out: https://djhworld.github.io/hyperloglog/
e.g. currently, it's doing:
[input] -> [3 hashes] -> [single storage]
but, I'm wondering if it's better to do: [input]
-> [hash a] -> [storage a]
-> [hash b] -> [storage b]
-> [hash c] -> [storage c]
...this way, the output of the 3 hashes don't affect each other during the set and membership-check. I wonder how that would affect how much more data you can store. I'm sure someone has considered this.