Фильтр Блума
Вспомним про абстрактную структуру данных множество. Для её адекватной реализации в любом случае требуется достаточно много памяти. Мы можем попытаться ослабить это требования за счёт возможности ошибаться.
Мы хотим создать такую структуру данных, в которую можно будет добавлять объекты и пытаться определять наличие в ней объектов. На запрос «есть ли в множестве объект ?» можно получить или определенный отрицательный ответ «объекта точно нет» или неопределенный положительный ответ «объект , возможно, в множестве есть».
Пусть все наши объекты — элементы какого-то универсума .
Про удаление объектов из нашей структуры пока не думаем и не говорим.
Реализацией нашей идеи служит структура фильтр Блума (Bloom filter). Эта структура данных использует хеширование.
Заведем битовый массив , состоящий из элементов. Этот битовый массив будет хранить информацию о наличии и отсутствии объектов в нашем множестве.
Создадим еще независимых хеш-функций .
const array h[k]
array[bool] b[m]Добавление элемента
При добавлении элемента в множество нужно записать в массив на каждую из позиций .
function add(x):
for int i = 0; i < k:
int index = h[i](x)
b[index] = trueВременная сложность выполнения операции — .
Проверка наличия
Чтобы проверить, что элемент принадлежит множеству, нужно проверить, что все биты в массиве на позициях установлены в . Если хотя бы один бит равен , то элемента в множестве быть не может.
function check(x) -> bool:
bool contains = true
for int i = 0; i < k:
int index = h[i](x)
contains &= b[index]
return containsВременная сложность выполнения операции — .
Как уже упоминалось выше, операция check(x) может вернуть или определенный отрицательный ответ,
или неопределенный положительный.
Из-за этого свойства фильтр Блума нельзя использовать напрямую, если важна точность.
Но он очень сильно пригождается тогда, когда надо выполнять много дорогостоящих запросов к другой структуре.
Например, есть у нас какая-то база данных (на самом деле любая структура данных) с дорогим доступом к элементам. Создадим поверх этой базы фильтр Блума, добавив в него все элементы, которые есть в базе данных. Теперь, когда нам приходит запрос к несуществующим данным, мы его отфильтруем фильтром Блума и не пустим дальше к базе данных. В итоге у нас значительно уменьшилось количество дорогостоящих запросов к базе данных ценой небольшого количества дополнительной памяти на фильтр.
Вероятность ошибки и её минимизация
Давайте посчитаем вероятность ложного положительного срабатывания операции check(x).
Как и прежде, размер битового массива , количество хеш-функций .
Пусть в нашем множестве уже содержится элементов.
Посчитаем вероятность того, что конкретный бит не будет установлен в хеш-функцией при вставке очередного элемента .
Тогда для хеш-функций вероятность того, что конкретный бит не будет установлен в равна
Теперь можем посчитать вероятность того, что -ый бит будет равен после вставки элементов в изначально пустой фильтр Блума
Ложное положительное срабатывание check(x) происходит тогда,
когда для несуществующего элемента все бит окажутся ненулевыми.
Вероятность такого события равна
Можно вычислить оптимальное значение , которое минимизирует ошибку. В таком виде задача минимизации не решается, придется прибегнуть к асимптотическим оценкам. Используя второй замечательный предел, можно упростить
Тогда
И вот уже в такой форме можно найти такое , при котором вероятность ошибки минимальная. Достаточно прологарифмировать вероятность ошибки и найти ноль производной. В итоге получается
Подставив это значение можно найти эту самую минимальную вероятность ошибки