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