# теория · шаг 3 из 7
Коллизии неизбежны: Дирихле и парадокс дней рождения
Коротко
Коллизия - это когда два разных ключа получают один номер ячейки: h(k₁) = h(k₂).
Их не «плохая функция придумала» - они НЕИЗБЕЖНЫ. Если ключей больше, чем ячеек (n > m),
хотя бы одна ячейка получит два ключа - это принцип Дирихле («голубей больше, чем
клеток»). Но хуже: коллизии появляются ЗАДОЛГО до заполнения - уже при ≈ √m ключах их
вероятность близка к половине. Это парадокс дней рождения: в группе всего из 23 человек
двое совпадут по дню рождения с вероятностью больше 50%, хотя дней 365. Значит хеш-таблица
ОБЯЗАНА уметь разрешать коллизии - это не редкий сбой, а штатная ситуация.
Если ты понимаешь, почему при n > m коллизия гарантирована (Дирихле) и почему уже при ≈√m ключах она вероятна (парадокс дней рождения), листай до «Теперь сам».
Как думаешь, можно ли подобрать хеш-функцию без коллизий
Заманчиво: а вдруг есть настолько умная хеш-функция, что разные ключи никогда не столкнутся?
Прикинем. Ключей может быть много - скажем, все возможные строки или все числа-паспорта, -
а ячеек в таблице ограниченное число m. Функция отображает БОЛЬШОЕ множество ключей в
МАЛЕНЬКОЕ множество номеров. По простому счёту: нельзя разным элементам большого множества
раздать разные номера из маленького - номеров не хватит. Значит какие-то ключи ОБЯЗАНЫ
поделить номер. Вопрос не «бывают ли коллизии» (бывают всегда), а «как рано они начинаются
и что с ними делать». И ответ на «как рано» окажется неожиданно неприятным.
На что это похоже
Комната, где собрались люди, и ты споришь, найдутся ли двое с одним днём рождения. Кажется, что для этого нужно человек 200 - ведь дней в году 365. Но нет: достаточно 23 человек, и шансы уже выше 50%. Почему так мало? Потому что важны не совпадения с ТОБОЙ, а совпадения МЕЖДУ ЛЮБОЙ парой - а пар в группе из 23 человек целых 253. Хеш-таблица - та же комната: «дни рождения» - это номера ячеек, «люди» - ключи. Совпадений между парами ключей набирается пугающе быстро, и первая коллизия случается куда раньше, чем таблица заполнится.
Механизм: два уровня неизбежности
Уровень 1 - принцип Дирихле (гарантия при n > m). Если раскладываешь n предметов
по m ящикам и n > m, то хотя бы в одном ящике окажется не меньше двух предметов. Иначе
в каждом ящике было бы не больше одного, и всего предметов - не больше m, противоречие.
Для хеша: если ключей больше, чем ячеек, коллизия ГАРАНТИРОВАНА при любой хеш-функции. Это
жёсткий верхний рубеж.
Уровень 2 - парадокс дней рождения (вероятно уже при ≈ √m). Задолго до n = m
коллизии становятся ВЕРОЯТНЫ. Прикинем вероятность, что при вбрасывании n ключей в m
ячеек все попадут в РАЗНЫЕ. Пар ключей примерно n²/2, каждая пара сталкивается с
вероятностью 1/m, поэтому ожидаемое число коллизий ≈ n²/(2m). Оно достигает единицы,
когда n ≈ √(2m) - то есть коллизия становится ожидаемой уже при числе ключей порядка
КОРНЯ из числа ячеек, а не самого числа ячеек.
Правило
Коллизии (h(k₁) = h(k₂) при k₁ ≠ k₂) НЕИЗБЕЖНЫ. При n > m коллизия гарантирована
любой хеш-функцией - принцип Дирихле (предметов больше, чем ящиков). Но вероятной коллизия
становится куда раньше: ожидаемое число коллизий ≈ n²/(2m) достигает единицы уже при
n ≈ √(2m) (то есть порядка √m) - парадокс дней рождения. Поэтому хеш-таблица обязана иметь механизм РАЗРЕШЕНИЯ
коллизий (следующая глава): они не аварийный случай, а норма работы.
Разбор: откуда берётся √m в парадоксе дней рождения
Разберём, почему именно КОРЕНЬ. Наивно кажется, что коллизия появится, когда ключей станет
сравнимо с числом ячеек m. Но считать надо не ключи, а ПАРЫ ключей - ведь сталкиваются
пары. Из n ключей пар примерно n·(n−1)/2 ≈ n²/2. Каждая конкретная пара при равномерной
функции попадает в одну ячейку с вероятностью 1/m. Ожидаемое число столкнувшихся пар -
это число пар на вероятность одной: (n²/2)·(1/m) = n²/(2m). Приравняем к 1 (в среднем одна
коллизия): n² ≈ 2m, откуда n ≈ √(2m). Вот и корень: квадрат в числе пар «съедает»
половину показателя. Для дней рождения m = 365, √(2·365) ≈ 27 - и правда, около двух-
трёх десятков человек хватает. Смысл для хеша: даже полупустая таблица уже полна коллизий,
и рассчитывать «пока ячеек больше ключей - столкновений нет» - грубая ошибка.
Разбор: почему это не вина хеш-функции
Легко подумать, что коллизии - признак ПЛОХОЙ хеш-функции, и хорошая их избежит. Это не так,
и различать тут важно. Идеально РАВНОМЕРНАЯ функция даёт МИНИМУМ коллизий - но не ноль:
парадокс дней рождения посчитан как раз для идеально случайного разбрасывания, и он всё
равно даёт коллизии при ≈ √m ключах. То есть даже совершенная функция сталкивает ключи -
просто реже, чем плохая. Плохая функция добавляет к неизбежным коллизиям ещё и лишние
(перекос в пару ячеек), а хорошая держит их на теоретическом минимуме. Поэтому цель хорошей
хеш-функции - не «убрать коллизии» (невозможно), а «не создавать ЛИШНИХ сверх неизбежных».
А раз коллизии всё равно будут, таблице нужен способ с ними жить - и это отдельная
инженерная задача, а не следствие кривой функции.
Что ломается без этого
Первая ошибка - строить хеш-таблицу без разрешения коллизий, в надежде «подберу функцию без
столкновений»: такой функции для n > m не существует (Дирихле), а на практике столкновения
начинаются уже при √m ключах. Второе - брать таблицу «с запасом, ячеек вдвое больше
ключей» и думать, что коллизий не будет: при n = m/2 ожидаемое число коллизий ≈ m/8 -
их сотни в таблице на тысячу ячеек. Третье - при подсчёте вероятности коллизии считать
совпадения с ОДНИМ ключом, а не между всеми парами: занизишь оценку в разы и удивишься, что
столкновения так часты. Четвёртое - винить хеш-функцию за коллизии как таковые: минимум
коллизий неустраним, вопрос лишь в лишних.
Зачем это в работе
Понимание неизбежности коллизий - основа грамотной работы с хеш-таблицами. Оно объясняет,
почему любая реальная реализация словаря ВСЕГДА содержит код разрешения коллизий (цепочки
или пробирование - следующая глава): без него структура просто неверна. Парадокс дней
рождения выходит далеко за хеш-таблицы: на нём стоят атаки на хеш-функции в криптографии
(найти коллизию в n-битном хеше можно за ≈ 2^(n/2) попыток, а не 2^n - поэтому для
защиты берут вдвое длиннее), оценки уникальности идентификаторов (сколько UUID можно
выдать, пока не столкнутся два), дедупликация. А практический вывод для хеш-таблицы прост:
держи заполненность (следующий урок про α) в узде и не жди, что «пустая таблица - без
коллизий». Коллизия - не сбой, а фон, на котором работает весь механизм.
Теперь сам
В таблице m = 100 ячеек. Наивно кажется, что коллизии начнутся около n = 100 ключей.
При каком числе ключей коллизия станет ОЖИДАЕМОЙ на самом деле, и почему это так рано?
Ответ. Ожидаемое число коллизий ≈ n²/(2m) = n²/200. Оно достигает единицы при
n² ≈ 200, то есть n ≈ 14 - всего четырнадцать ключей на сто ячеек, таблица заполнена на
14%! Это ≈ √(2m) = √200 ≈ 14 - тот самый корень из парадокса дней рождения. Так рано
потому, что сталкиваются ПАРЫ ключей, а пар из 14 ключей уже 14·13/2 = 91 - почти сотня,
и при вероятности 1/100 на пару одна из них наверняка совпадёт. Наивная оценка «около 100»
считает ключи, а надо считать пары - их квадратично много, поэтому первая коллизия приходит
на корне от числа ячеек, а не при их заполнении. Мораль: даже сильно недозаполненная
таблица уже живёт с коллизиями.
Главное
Коллизия (h(k₁) = h(k₂), k₁ ≠ k₂) НЕИЗБЕЖНА. При n > m она гарантирована любой
функцией - принцип Дирихле (предметов больше ящиков). Но вероятной становится куда раньше:
ожидаемое число коллизий ≈ n²/(2m) достигает единицы уже при n ≈ √(2m) (порядка √m) - парадокс дней
рождения (23 человека → совпадение дня рождения с шансом >50%, потому что важны ПАРЫ, а их
≈ n²/2). Даже идеально равномерная функция даёт минимум коллизий, а не ноль - цель хорошей
функции не убрать их (нельзя), а не создавать лишних. Значит хеш-таблице ОБЯЗАТЕЛЬНО нужен
механизм разрешения коллизий - это норма, а не сбой.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий