Теория чисел · модуль 9 из 14
Остаток от деления: как устроена хеш-таблица
Ключ попадает в корзину с номером k mod m. Если у шага между ключами есть общий делитель с m, ключи скапливаются в немногих корзинах. Сравните m = 10 и m = 11.
- первый ключ в корзине
- коллизия
Теория
Остаток от деления $k \bmod m$ — число $r$ из диапазона $0 \dots m-1$, для которого $k = q m + r$. Хеш-таблица с методом цепочек хранит $m$ корзин и кладёт ключ $k$ в корзину с номером
$$ h(k) = k \bmod m . $$
Если в одну корзину попадает несколько ключей, возникает коллизия: ключи образуют цепочку, и поиск в корзине требует перебора. Средняя длина цепочки равна коэффициенту заполнения $\alpha = n / m$, где $n$ — число ключей.
Скопления. Пусть ключи идут с шагом $s$: $k, k + s, k + 2s, \dots$ Их остатки по модулю $m$ повторяются с периодом $m / \gcd(s, m)$, поэтому ключи попадают не более чем в $m / \gcd(s, m)$ корзин. При $s = 10$ и $m = 10$ все ключи оказываются в одной корзине, а при простом $m = 11$ они распределяются равномерно. Поэтому в качестве $m$ часто выбирают простое число.
При равномерном распределении поиск, вставка и удаление выполняются в среднем за $O(1 + \alpha)$. Если все ключи собрались в одной цепочке, поиск деградирует до $O(n)$.
Где это применяется в IT
dict и set в Python, HashMap в Java, индексы баз данных и шардирование данных по серверам раскладывают ключи по корзинам хеш-функцией. В основе многих хеш-функций лежит остаток от деления.
- Рост таблицы. Python увеличивает таблицу
dict, когда она заполнена на две трети, чтобы $\alpha$ оставался малым. - Простой модуль. Простое $m$ устраняет скопления, если ключи идут с постоянным шагом.
- Деградация. Длинная цепочка превращает поиск за $O(1)$ в поиск за $O(n)$; на этом основаны атаки на веб-серверы с подбором коллизий.
Код на Python
Загружаю пример…