0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>Проверка наличия объекта - одна из ключевых задач в программировании. Она встречается при разработке практически любого программного продукта. Чтобы ускорить поиск объекта, разработчики применяют фильтр Блума. В этой статье мы расскажем, почему появился фильтр Блума и в каких случаях его применяют. Также познакомим с принципами работы с фильтром и научим пользоваться им.</strong></p>
1
<p><strong>Проверка наличия объекта - одна из ключевых задач в программировании. Она встречается при разработке практически любого программного продукта. Чтобы ускорить поиск объекта, разработчики применяют фильтр Блума. В этой статье мы расскажем, почему появился фильтр Блума и в каких случаях его применяют. Также познакомим с принципами работы с фильтром и научим пользоваться им.</strong></p>
2
<p>Эта тема не входит в стандартную программу подготовки программистов. Если вы изучите ее и примените на практике, то станете более востребованным специалистом.</p>
2
<p>Эта тема не входит в стандартную программу подготовки программистов. Если вы изучите ее и примените на практике, то станете более востребованным специалистом.</p>
3
<h2>Содержание</h2>
3
<h2>Содержание</h2>
4
<ul><li><a>Как ищут объект</a></li>
4
<ul><li><a>Как ищут объект</a></li>
5
<li><a>Что такое фильтр Блума и как он работает</a></li>
5
<li><a>Что такое фильтр Блума и как он работает</a></li>
6
<li><a>Какие у фильтра Блума недостатки и ограничения</a></li>
6
<li><a>Какие у фильтра Блума недостатки и ограничения</a></li>
7
<li><a>Где применяется фильтр Блума</a></li>
7
<li><a>Где применяется фильтр Блума</a></li>
8
<li><a>Как фильтр Блума реализуется на JavaScript</a></li>
8
<li><a>Как фильтр Блума реализуется на JavaScript</a></li>
9
<li><a>Вывод</a></li>
9
<li><a>Вывод</a></li>
10
<li><a>Дополнительные материалы</a></li>
10
<li><a>Дополнительные материалы</a></li>
11
</ul><h2>Как ищут объект</h2>
11
</ul><h2>Как ищут объект</h2>
12
<p>Чтобы найти объект, применяют разные способы. Можно сделать последовательный перебор или использовать хеш-таблицы. При этом каждый из этих способов применяется в определенных случаях, и у них есть ограничения.</p>
12
<p>Чтобы найти объект, применяют разные способы. Можно сделать последовательный перебор или использовать хеш-таблицы. При этом каждый из этих способов применяется в определенных случаях, и у них есть ограничения.</p>
13
<p>Например, поиск в таблице маршрутизации на роутере должен быть максимально быстрым, притом что скорость процессора и доступная память устройства ограничены.</p>
13
<p>Например, поиск в таблице маршрутизации на роутере должен быть максимально быстрым, притом что скорость процессора и доступная память устройства ограничены.</p>
14
<p>Предположим, вы играете с другом в "Города". Вы плохо знаете географию и поэтому не можете утверждать, что Норфолк - это не город.</p>
14
<p>Предположим, вы играете с другом в "Города". Вы плохо знаете географию и поэтому не можете утверждать, что Норфолк - это не город.</p>
15
<p>Программист решил бы эту проблему так: разработал программу, загрузил в нее справочник всех городов планеты и выполнил поиск. Но это не понравится сопернику, так как ему придется долго ждать.</p>
15
<p>Программист решил бы эту проблему так: разработал программу, загрузил в нее справочник всех городов планеты и выполнил поиск. Но это не понравится сопернику, так как ему придется долго ждать.</p>
16
<p>Допустим, поиск города в неотсортированном списке занимает время О(N). Всего в мире насчитывается 2 667 417 городов. Предположим, что наш компьютер может выполнять 1 000 операций в секунду. Тогда время на поиск составит 2667,5 секунды - более 44 минут.</p>
16
<p>Допустим, поиск города в неотсортированном списке занимает время О(N). Всего в мире насчитывается 2 667 417 городов. Предположим, что наш компьютер может выполнять 1 000 операций в секунду. Тогда время на поиск составит 2667,5 секунды - более 44 минут.</p>
17
<p>Это слишком долго для игры в "Города", поэтому нужно ускорить процесс. Например, купить компьютер помощнее, но тогда придется потратиться. Решить вопрос можно менее затратно - улучшить алгоритм, который сократит время поиска.</p>
17
<p>Это слишком долго для игры в "Города", поэтому нужно ускорить процесс. Например, купить компьютер помощнее, но тогда придется потратиться. Решить вопрос можно менее затратно - улучшить алгоритм, который сократит время поиска.</p>
18
<p>Например, можно использовать хеш-таблицы, чья скорость поиска в идеальном случае считается равной О(1). Только она требует больших затрат по памяти и зависит от выбранного разработчиком метода хеширования.</p>
18
<p>Например, можно использовать хеш-таблицы, чья скорость поиска в идеальном случае считается равной О(1). Только она требует больших затрат по памяти и зависит от выбранного разработчиком метода хеширования.</p>
19
<p>Из-за того, что в программировании не было идеального способа решения такой проблемы, придумали вероятностную структуру данных - фильтр Блума. С его помощью не ищут объект, а проверяют, что его не существует. Еще он решает поставленную задачу за время О(1) и не размещает все объекты в оперативной памяти.</p>
19
<p>Из-за того, что в программировании не было идеального способа решения такой проблемы, придумали вероятностную структуру данных - фильтр Блума. С его помощью не ищут объект, а проверяют, что его не существует. Еще он решает поставленную задачу за время О(1) и не размещает все объекты в оперативной памяти.</p>
20
<h2>Что такое фильтр Блума и как он работает</h2>
20
<h2>Что такое фильтр Блума и как он работает</h2>
21
<p>Фильтр Блума - это инструмент разработчика, который ускоряет проверку наличия объекта.</p>
21
<p>Фильтр Блума - это инструмент разработчика, который ускоряет проверку наличия объекта.</p>
22
<p>Фильтр состоит из двух частей: нескольких хеш-функций, которые преобразуют элементы, добавляемые в фильтр, и массива битов. Результатом вычисления хеш-функций будут позиции в массиве, значения которых будут равны единице.</p>
22
<p>Фильтр состоит из двух частей: нескольких хеш-функций, которые преобразуют элементы, добавляемые в фильтр, и массива битов. Результатом вычисления хеш-функций будут позиции в массиве, значения которых будут равны единице.</p>
23
<p>Проверка вхождения проходит аналогично: к искомому элементу применяются те же хеш-функции. При этом номера полученных адресов сравниваются с соответствующими адресами в массиве битов.</p>
23
<p>Проверка вхождения проходит аналогично: к искомому элементу применяются те же хеш-функции. При этом номера полученных адресов сравниваются с соответствующими адресами в массиве битов.</p>
24
<p>Далее события могут развиваться по двум вариантам:</p>
24
<p>Далее события могут развиваться по двум вариантам:</p>
25
<ol><li><p>Хотя бы в одном из адресов содержится ноль - объекта нет в исходной последовательности.</p>
25
<ol><li><p>Хотя бы в одном из адресов содержится ноль - объекта нет в исходной последовательности.</p>
26
</li>
26
</li>
27
<li><p>Во всех адресах содержатся только единицы - искомый объект, возможно, присутствует в исходной последовательности.</p>
27
<li><p>Во всех адресах содержатся только единицы - искомый объект, возможно, присутствует в исходной последовательности.</p>
28
</li>
28
</li>
29
</ol><p>Например, так выглядит фильтр последовательности трех элементов - x, y и z:</p>
29
</ol><p>Например, так выглядит фильтр последовательности трех элементов - x, y и z:</p>
30
<p>Фильтр состоит из трех хеш-функций и битового массива длиной 18. Когда элемент добавляется, к нему применяется по три хеш-функции - они возвращаются в результате позиции в массиве.</p>
30
<p>Фильтр состоит из трех хеш-функций и битового массива длиной 18. Когда элемент добавляется, к нему применяется по три хеш-функции - они возвращаются в результате позиции в массиве.</p>
31
<p>В местах, где указываются функции, устанавливаем значение элемента массива равным единице. Далее проверяется наличие элемента w.</p>
31
<p>В местах, где указываются функции, устанавливаем значение элемента массива равным единице. Далее проверяется наличие элемента w.</p>
32
<p>В нашем примере проверка завершается неудачно, так как хеш-функции вернули позиции массива, в одной из которых содержится ноль.</p>
32
<p>В нашем примере проверка завершается неудачно, так как хеш-функции вернули позиции массива, в одной из которых содержится ноль.</p>
33
<p>Вернемся к игре, где один из соперников назвал город Норфолк. Чтобы узнать, есть ли такой город, применим фильтр Блума.</p>
33
<p>Вернемся к игре, где один из соперников назвал город Норфолк. Чтобы узнать, есть ли такой город, применим фильтр Блума.</p>
34
<p>Составим полный список всех городов мира и применим для каждого элемента хеш-функции. У нас получится следующий массив:</p>
34
<p>Составим полный список всех городов мира и применим для каждого элемента хеш-функции. У нас получится следующий массив:</p>
35
<p>Проверим работу программы для нашего списка на Москве:</p>
35
<p>Проверим работу программы для нашего списка на Москве:</p>
36
<p>Во всех адресах содержатся единицы - город Москва, возможно, есть в списке.</p>
36
<p>Во всех адресах содержатся единицы - город Москва, возможно, есть в списке.</p>
37
<p>Теперь проверим наличие Норфолка в базе:</p>
37
<p>Теперь проверим наличие Норфолка в базе:</p>
38
<p>Одна из ячеек содержит ноль - города Норфолк нет в списке. Чтобы вычислить это, нам понадобились доли секунды, а не 40 минут для перебора каждого элемента списка.</p>
38
<p>Одна из ячеек содержит ноль - города Норфолк нет в списке. Чтобы вычислить это, нам понадобились доли секунды, а не 40 минут для перебора каждого элемента списка.</p>
39
<p>Фильтр Блума ускоряет поиск объекта, что важно для разработки. При этом у него есть побочный эффект - результат бывает неопределенным. Например, когда хеш-функции вернули все позиции массива с единицей. Это означает, что объект, возможно, есть в исходной последовательности. Такой побочный эффект - главный недостаток фильтра Блума.</p>
39
<p>Фильтр Блума ускоряет поиск объекта, что важно для разработки. При этом у него есть побочный эффект - результат бывает неопределенным. Например, когда хеш-функции вернули все позиции массива с единицей. Это означает, что объект, возможно, есть в исходной последовательности. Такой побочный эффект - главный недостаток фильтра Блума.</p>
40
<h2>Какие у фильтра Блума недостатки и ограничения</h2>
40
<h2>Какие у фильтра Блума недостатки и ограничения</h2>
41
<p>У фильтра Блума есть важное преимущество: он экономит память и увеличивает скорость проверки информации о наличии конкретного объекта. Но из-за этого проявляется существенный недостаток.</p>
41
<p>У фильтра Блума есть важное преимущество: он экономит память и увеличивает скорость проверки информации о наличии конкретного объекта. Но из-за этого проявляется существенный недостаток.</p>
42
<p>Фильтр работает с косвенными данными, которые вычисляются на основе хеш-функций. Они подвергаются<strong>коллизиям</strong>- когда при разных входных документах с некоторой частотой выдаются одинаковые ответы.</p>
42
<p>Фильтр работает с косвенными данными, которые вычисляются на основе хеш-функций. Они подвергаются<strong>коллизиям</strong>- когда при разных входных документах с некоторой частотой выдаются одинаковые ответы.</p>
43
<p>Из-за этого недостатка у фильтра Блума появляется ограничение -<strong>вероятность ошибки</strong>. Фильтр отвечает, что объект, возможно, есть, хотя его нет в изначальной последовательности.</p>
43
<p>Из-за этого недостатка у фильтра Блума появляется ограничение -<strong>вероятность ошибки</strong>. Фильтр отвечает, что объект, возможно, есть, хотя его нет в изначальной последовательности.</p>
44
<p>Вероятность ошибки можно уменьшить, если подобрать оптимальное количество хеш-функций, которые обрабатывают входную последовательность. Еще вероятность ошибки можно снизить, если увеличить размерность массива битов.</p>
44
<p>Вероятность ошибки можно уменьшить, если подобрать оптимальное количество хеш-функций, которые обрабатывают входную последовательность. Еще вероятность ошибки можно снизить, если увеличить размерность массива битов.</p>
45
<p>Представим, что мы продолжаем играть в "Города" и решили уменьшить вероятность ложноположительного ответа.</p>
45
<p>Представим, что мы продолжаем играть в "Города" и решили уменьшить вероятность ложноположительного ответа.</p>
46
<p>Если мы подберем оптимальное количество хеш-функций, вероятность ошибки составит 0,001 - 0,1%. Это значение нашли через<a>вероятность ложноположительного срабатывания</a>.</p>
46
<p>Если мы подберем оптимальное количество хеш-функций, вероятность ошибки составит 0,001 - 0,1%. Это значение нашли через<a>вероятность ложноположительного срабатывания</a>.</p>
47
<p>Получается, что на каждую тысячу названных игроками городов только один будет определен некорректно - возможно существующий. Это можно признать допустимым для игры.</p>
47
<p>Получается, что на каждую тысячу названных игроками городов только один будет определен некорректно - возможно существующий. Это можно признать допустимым для игры.</p>
48
<p>Фильтр Блума все равно выдаст неопределенный ответ, если уменьшить вероятность ошибки.</p>
48
<p>Фильтр Блума все равно выдаст неопределенный ответ, если уменьшить вероятность ошибки.</p>
49
<p>Если нам нужен точный ответ, то можно выполнить традиционный поиск в исходной последовательности. Поэтому классические методы выбора оптимального алгоритма поиска все равно применяются.</p>
49
<p>Если нам нужен точный ответ, то можно выполнить традиционный поиск в исходной последовательности. Поэтому классические методы выбора оптимального алгоритма поиска все равно применяются.</p>
50
<p>Фильтр Блума сэкономит время на обращение к диску и уменьшит их частоту. Но это можно применить только в качестве предварительного фильтра запросов к основной последовательности.</p>
50
<p>Фильтр Блума сэкономит время на обращение к диску и уменьшит их частоту. Но это можно применить только в качестве предварительного фильтра запросов к основной последовательности.</p>
51
<p>В таком виде структура данных используется в поисковых движках типа Google. С ее помощью можно не обращаться к ресурсам, на которых гарантированно отсутствует информация из поискового запроса.</p>
51
<p>В таком виде структура данных используется в поисковых движках типа Google. С ее помощью можно не обращаться к ресурсам, на которых гарантированно отсутствует информация из поискового запроса.</p>
52
<p>Несмотря на то, что фильтр Блума придумали более 50 лет назад, он до сих пор активно применяется разработчиками. Рассмотрим, где с ним можно столкнуться.</p>
52
<p>Несмотря на то, что фильтр Блума придумали более 50 лет назад, он до сих пор активно применяется разработчиками. Рассмотрим, где с ним можно столкнуться.</p>
53
<h2>Где применяется фильтр Блума</h2>
53
<h2>Где применяется фильтр Блума</h2>
54
<p>По сравнению со старыми компьютерами, вычислительная мощность современных устройств выросла. При этом увеличилась и сложность задач. Некоторые из них могут решаться несколько дней из-за больших данных. В итоге запросы долго обрабатываются, памяти не хватает, а оборудование быстро изнашивается.</p>
54
<p>По сравнению со старыми компьютерами, вычислительная мощность современных устройств выросла. При этом увеличилась и сложность задач. Некоторые из них могут решаться несколько дней из-за больших данных. В итоге запросы долго обрабатываются, памяти не хватает, а оборудование быстро изнашивается.</p>
55
<p>В этом случае помогает фильтр Блума, который применяется в следующих случаях:</p>
55
<p>В этом случае помогает фильтр Блума, который применяется в следующих случаях:</p>
56
<ol><li><p><strong>Поиск в интернете</strong>. Здесь фильтр позволяет поисковой машине не сканировать ресурсы, на которых точно нет информации из поискового запроса. Это экономит время пользователя.</p>
56
<ol><li><p><strong>Поиск в интернете</strong>. Здесь фильтр позволяет поисковой машине не сканировать ресурсы, на которых точно нет информации из поискового запроса. Это экономит время пользователя.</p>
57
</li>
57
</li>
58
<li><p><strong>Таблицы маршрутизации</strong>. Например, черные списки IP-адресов, куда нельзя перенаправлять запросы. Эту информацию нужно получать максимально быстро. Ниже на рисунке пример проверки двух IP-адресов в черном списке:</p>
58
<li><p><strong>Таблицы маршрутизации</strong>. Например, черные списки IP-адресов, куда нельзя перенаправлять запросы. Эту информацию нужно получать максимально быстро. Ниже на рисунке пример проверки двух IP-адресов в черном списке:</p>
59
</li>
59
</li>
60
</ol><p>Адреса 112.64.90.12 точно нет в списке, так как одна из функций вернула позицию с нулевым значением. Для адреса 178.23.12.63 нужно уточнение, так как фильтр отвечает, что адрес, возможно, есть в черном списке. Если бы маршрутизатор проверял каждый из адресов полностью, скорость работы интернета была бы значительно ниже</p>
60
</ol><p>Адреса 112.64.90.12 точно нет в списке, так как одна из функций вернула позицию с нулевым значением. Для адреса 178.23.12.63 нужно уточнение, так как фильтр отвечает, что адрес, возможно, есть в черном списке. Если бы маршрутизатор проверял каждый из адресов полностью, скорость работы интернета была бы значительно ниже</p>
61
<ol><li><p><strong>Системы проверки орфографии</strong>. В этом случае компьютер может выделить слово, которого нет в словаре. Так не придется нагружать устройство после каждого нажатия клавиши.</p>
61
<ol><li><p><strong>Системы проверки орфографии</strong>. В этом случае компьютер может выделить слово, которого нет в словаре. Так не придется нагружать устройство после каждого нажатия клавиши.</p>
62
</li>
62
</li>
63
<li><p><strong>Криптокошельки</strong>. Фильтр Блума используется, чтобы искать транзакции. В результате с некоторой вероятностью можно утверждать, что транзакция в наличии, или ее нет.</p>
63
<li><p><strong>Криптокошельки</strong>. Фильтр Блума используется, чтобы искать транзакции. В результате с некоторой вероятностью можно утверждать, что транзакция в наличии, или ее нет.</p>
64
</li>
64
</li>
65
<li><p><strong>В биоинформатике</strong>. Фильтр используется, чтобы искать фрагменты в последовательностях ДНК. Ниже на рисунке видно, как в фильтр Блума добавили цепочку ДНК ACCTAG и искали фрагмент CGTAT:</p>
65
<li><p><strong>В биоинформатике</strong>. Фильтр используется, чтобы искать фрагменты в последовательностях ДНК. Ниже на рисунке видно, как в фильтр Блума добавили цепочку ДНК ACCTAG и искали фрагмент CGTAT:</p>
66
</li>
66
</li>
67
</ol><p><em>Поиск завершается неудачей, так как различается последняя буква в искомой последовательности аминокислот и исходной последовательности ДНК</em></p>
67
</ol><p><em>Поиск завершается неудачей, так как различается последняя буква в искомой последовательности аминокислот и исходной последовательности ДНК</em></p>
68
<p>Список случаев, где применяется фильтр Блума, постоянно пополняется. В сети можно найти публикации об открытии новых мест применения этой структуры данных.</p>
68
<p>Список случаев, где применяется фильтр Блума, постоянно пополняется. В сети можно найти публикации об открытии новых мест применения этой структуры данных.</p>
69
<p>Теперь посмотрим, как фильтр Блума применяется на практике. Поработаем с JavaScript.</p>
69
<p>Теперь посмотрим, как фильтр Блума применяется на практике. Поработаем с JavaScript.</p>
70
<h2>Как фильтр Блума реализуется на JavaScript</h2>
70
<h2>Как фильтр Блума реализуется на JavaScript</h2>
71
<p>Работу с фильтром Блума будем рассматривать по шагам.</p>
71
<p>Работу с фильтром Блума будем рассматривать по шагам.</p>
72
<ol><li>Для начала создадим новый класс, который будет представлять нашу структуру данных. Также создадим к нему конструктор, который в качестве исходных параметров будет принимать размерность массива и количество используемых хеш-функций.</li>
72
<ol><li>Для начала создадим новый класс, который будет представлять нашу структуру данных. Также создадим к нему конструктор, который в качестве исходных параметров будет принимать размерность массива и количество используемых хеш-функций.</li>
73
</ol><p>В свойствах конструктора сохраним входные значения и создадим массив, который проинициализируем нулевыми значениями:</p>
73
</ol><p>В свойствах конструктора сохраним входные значения и создадим массив, который проинициализируем нулевыми значениями:</p>
74
<ol><li>Далее реализуем операцию, которая добавляет элемент исходной последовательности в фильтр. Для этого захешируем исходный объект N раз с помощью различных хеш-функций и по каждому полученному адресу установим значение бита равным единице.</li>
74
<ol><li>Далее реализуем операцию, которая добавляет элемент исходной последовательности в фильтр. Для этого захешируем исходный объект N раз с помощью различных хеш-функций и по каждому полученному адресу установим значение бита равным единице.</li>
75
</ol><p>Чтобы упростить реализацию, мы используем одну хеш-функцию. Она будет незначительно изменять алгоритм хеширования в зависимости от номера функции, с которым ее вызвали.</p>
75
</ol><p>Чтобы упростить реализацию, мы используем одну хеш-функцию. Она будет незначительно изменять алгоритм хеширования в зависимости от номера функции, с которым ее вызвали.</p>
76
<p>Так как в JavaScript нет встроенного метода хеширования строки, будем использовать<a>метод String.hashCode()</a>.</p>
76
<p>Так как в JavaScript нет встроенного метода хеширования строки, будем использовать<a>метод String.hashCode()</a>.</p>
77
<p>Этот алгоритм переводит строку в число, которое суммируется из кодов символов в таблице кодировки. Так как фильтру Блума нужно несколько разных хеш-функций, мы модифицируем исходную строку. Для этого добавим в качестве префикса номера используемой хеш-функции:</p>
77
<p>Этот алгоритм переводит строку в число, которое суммируется из кодов символов в таблице кодировки. Так как фильтру Блума нужно несколько разных хеш-функций, мы модифицируем исходную строку. Для этого добавим в качестве префикса номера используемой хеш-функции:</p>
78
<ol><li>В конце проверим наличие элемента в последовательности. Для этого прогоним хеш-функции для искомого элемента и проверим по каждому адресу значение бита. Если хоть одно значение равно нулю, то элемента точно нет в последовательности:</li>
78
<ol><li>В конце проверим наличие элемента в последовательности. Для этого прогоним хеш-функции для искомого элемента и проверим по каждому адресу значение бита. Если хоть одно значение равно нулю, то элемента точно нет в последовательности:</li>
79
</ol><p>Если бы каждое значение равнялось единице, то элемент, возможно, был в последовательности.</p>
79
</ol><p>Если бы каждое значение равнялось единице, то элемент, возможно, был в последовательности.</p>
80
<h2>Вывод</h2>
80
<h2>Вывод</h2>
81
<p>Вероятностная структура данных фильтр Блума увеличивает скорость проверки информации и показывает, есть ли объект в последовательности.</p>
81
<p>Вероятностная структура данных фильтр Блума увеличивает скорость проверки информации и показывает, есть ли объект в последовательности.</p>
82
<p>Этот инструмент важен для разработчиков, так как не разбирается в стандартных программах обучения, но ценится среди специалистов.</p>
82
<p>Этот инструмент важен для разработчиков, так как не разбирается в стандартных программах обучения, но ценится среди специалистов.</p>
83
<p>Сегодня фильтр используют в поисковых движках, таблицах маршрутизации, системах проверки орфографии и крипто-кошельках. Уже сейчас этот список может быть шире, так как разработчики постоянно ищут новые способы, чтобы применить инструмент.</p>
83
<p>Сегодня фильтр используют в поисковых движках, таблицах маршрутизации, системах проверки орфографии и крипто-кошельках. Уже сейчас этот список может быть шире, так как разработчики постоянно ищут новые способы, чтобы применить инструмент.</p>
84
<p>Если хотите рассмотреть полную версию программной реализации, можете найти ее в<a>Github</a>. Так вы научитесь пользоваться фильтром Блума в полной мере и станете более востребованным специалистом.</p>
84
<p>Если хотите рассмотреть полную версию программной реализации, можете найти ее в<a>Github</a>. Так вы научитесь пользоваться фильтром Блума в полной мере и станете более востребованным специалистом.</p>
85
<h2>Дополнительные материалы</h2>
85
<h2>Дополнительные материалы</h2>
86
<ul><li><a>Пример реализации, разобранный в статье</a></li>
86
<ul><li><a>Пример реализации, разобранный в статье</a></li>
87
<li><a>Основная статья с изложением идей Бертона Блума</a></li>
87
<li><a>Основная статья с изложением идей Бертона Блума</a></li>
88
<li><a>Детали определения вероятности ложноположительного срабатывания</a></li>
88
<li><a>Детали определения вероятности ложноположительного срабатывания</a></li>
89
<li><a>Имплементация алгоритма хеширования строк для JS</a></li>
89
<li><a>Имплементация алгоритма хеширования строк для JS</a></li>
90
</ul><blockquote><h3>Никогда не останавливайтесь:</h3>
90
</ul><blockquote><h3>Никогда не останавливайтесь:</h3>
91
<p>В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами - на Хекслете есть<a>сотни курсов по разработке на разных языках и технологиях</a></p>
91
<p>В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами - на Хекслете есть<a>сотни курсов по разработке на разных языках и технологиях</a></p>
92
</blockquote>
92
</blockquote>