Хеширование
Хеширование — класс методов поиска, идея которого состоит в использовании информации, полученной из ключа (однозначно характеризующего элемент), как основа для поиска. С помощью хеш-функции мы вычисляем хеш-код, являющийся ключом, и используем его для проведения поиска; индексирование в памяти по хеш-коду происходит за
. В общем случае, однозначного соответствия между исходными данными и хеш-кодом нет в силу того, что количество значений хеш-функций меньше, чем вариантов исходных данных, поэтому существуют элементы, имеющие одинаковые хеш-коды — так называемые коллизии, но если два элемента имеют разный хеш-код, то они гарантированно различаются. Вероятность возникновения коллизий играет немаловажную роль в оценке качества хеш-функций.Определение: |
— называется хеш-функцией, где множество хранит ключи из множества . Если значит Коллизия: | — множество объектов (универсум).
Виды хеширования
- По способу хранения:
- Статическое — фиксированное количество элементов. Один раз заполняем хеш-таблицу и осуществляем только проверку на наличие в ней нужных элементов.
- Динамическое — добавляем, удаляем и смотрим на наличие нужных элементов.
- По виду хеш-функции:
- Детерминированная хеш-функция.
- Случайная хеш-функция.
Хеш-таблица
Хеш-табли́ца — структура данных, реализующая интерфейс ассоциативного массива. Представляет собой эффективную структуру данных для реализации словарей, а именно, она позволяет хранить пары (ключ, значение) и выполнять три операции: операцию добавления новой пары, операцию поиска и операцию удаления пары по ключу.
Введение
Существует два основных варианта хеш-таблиц: с цепочками и открытой адресацией. Хеш-таблица содержит некоторый массив
, элементы которого есть пары (хеш-таблица с открытой адресацией) или списки пар (хеш-таблица с цепочками).Выполнение операции в хеш-таблице начинается с вычисления хеш-функции от ключа. Получающееся хеш-значение
играет роль индекса в массиве . Затем, зная индекс, мы можем выполнить требующуюся операцию (добавление, удаление или поиск).Ситуация, когда для различных ключей получается одинаковое хеш-значение, встречается не так уж и редко, и зависит от хеш-функции. Чем лучше, используемая хеш-функция, тем меньше вероятность возникновения коллизии. При вставке в хеш-таблицу размером 365 ячеек всего лишь 23-х элементов вероятность коллизии превышает 50 % (при равномерном распределении значений хеш-функции)[1]. Способ разрешения коллизий — важная составляющая любой хеш-таблицы.
Полностью избежать коллизий для произвольных данных невозможно в принципе, и хорошая хеш-функция в состоянии только минимизировать их количество. Но, в некоторых специальных случаях их удаётся избежать. Если все ключи элементов известны заранее, либо меняются очень редко, то можно подобрать хеш-функцию, с помощью которой, все ключи будут распределены по хеш-таблице без коллизий. Это хеш-таблицы с прямой адресацией; в них все операции, такие как: поиск, вставка и удаление работают за
.Если мы поделим число хранимых элементов на размер массива
(число возможных значений хеш-функции), то узнаем коэффициент заполнения хеш-таблицы (англ. load factor). От этого параметра зависит среднее время выполнения операций.Свойства хеш-таблицы
На поиск элемента в хеш-таблице в худшем случае, может потребоваться столько же времени, как и в связанном списке, а именно перехешировать таблицу: увеличить размер массива и заново добавить в новую хеш-таблицу все пары.
, но на практике хеширование исключительно эффективно. При некоторых разумных допущениях математическое ожидание времени поиска элемента в хеш-таблице составляет . А все операции (поиск, вставка и удаление элементов) в среднем выполняются за время . При этом не гарантируется, что время выполнения отдельной операции мало́, так как при достижении некоторого значения коэффициента заполнения необходимоРазрешение коллизий
Открытое хеширование
Открытое хеширование или хеширование цепочками. Каждая ячейка
массива содержит указатель на начало списка всех элементов, хеш-значение ключа которых равно , либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.Время, необходимое для вставки в наихудшем случае равно
. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.Время работы поиска в наихудшем случае пропорционально длине списка, а если все
ключей хешированы в одну и ту же ячейку (создавая список длиной ) время поиска будет равно плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех элементов.Удаления элемента может быть выполнено за [2]
, как и вставка, при использовании двухсвязного списка.Закрытое хеширование
В случае метода открытой адресации (или по-другому — метод закрытого хеширования) все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании метода открытой адресации может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.
Рассмотрим один из методов борьбы с коллизиями.[3]
Линейное разрешение коллизий
В массиве
хранятся сами пары ключ-значение. Алгоритм вставки элемента проверяет ячейки массива в заданном порядке до тех пор, пока не будет найдена первая свободная ячейка, в неё и будет записан новый элемент. Это позволяет сэкономить память на хранение указателей.Последовательность, в которой просматриваются ячейки хеш-таблицы, называется последовательностью проб. В общем случае, она зависит только от ключа элемента, то есть это последовательность
, , ..., , где — ключ элемента, а — произвольные функции, сопоставляющие каждому ключу ячейку в хеш-таблице. Первый элемент в последовательности, как правило, равен значению некоторой хеш-функции от ключа, а остальные считаются от него каким-нибудь способом. Для успешной работы алгоритмов поиска последовательность проб должна быть такой, чтобы все ячейки хеш-таблицы оказались просмотренными ровно по одному разу.Алгоритм поиска просматривает ячейки хеш-таблицы в том же порядке, что и при вставке, пока не найдется элемент с искомым ключом, либо свободная ячейка (что означает отсутствие элемента в хеш-таблице).
Удаление элементов в такой схеме несколько затруднено. Можно поступить так: будем помечать каждую ячейку по признаку: удалили мы из неё элемент, или нет. В этом случае, удалением является установка метки «удалён», для соответсвующей ячейки хеш-таблицы. Остаётся только модифицировать поиск (если удалён, то занято) и вставку (если удалён, то пусто) элементов.
Примечания
- ↑ Парадокс дней рождения — Википедия
- ↑ Анализ хеширования с цепочками, вы можете найти в книге Томаса Кормена: «Алгоритмы. Построение и анализ.»
- ↑ Другой метод борьбы с коллизиями — двойное хеширование
Источники
- Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. «Алгоритмы. Построение и анализ» — «Вильямс», 2011 г. — 1296 стр. — ISBN 978-5-8459-0857-5, 5-8459-0857-4, 0-07-013151-1
- Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» — «Вильямс», 2007 г. — 824 стр. — ISBN 0-201-89685-0
- Хеширование — Википедия
- Хеш-таблица — Википедия