Идеальное хеширование — различия между версиями
(→Поиск) |
|||
Строка 1: | Строка 1: | ||
− | '''Двойное хеширование''' {{---}} метод борьбы с возникающими | + | '''Двойное хеширование''' {{---}} метод борьбы с коллизиями, возникающими при [[Открытое_и_закрытое_хеширование#Закрытое хеширование|закрытом хешировании]], основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы. |
==Принцип двойного хеширования== | ==Принцип двойного хеширования== | ||
− | При двойном хешировании используются две независимые хеш-функции <tex> h_1(k) </tex> и <tex> h_2(k) </tex>. Пусть <tex> k </tex> это наш ключ, <tex> m </tex> размер нашей таблицы, <tex> \mod m </tex> | + | При двойном хешировании используются две независимые хеш-функции <tex> h_1(k) </tex> и <tex> h_2(k) </tex>. Пусть <tex> k </tex> {{---}} это наш ключ, <tex> m </tex> {{---}} размер нашей таблицы, <tex>n \mod m </tex> {{---}} остаток от деления <tex> n </tex> на <tex> m </tex>, тогда сначала исследуется ячейка с адресом <tex> h_1(k) </tex>, если она уже занята, то рассматривается <tex> (h_1(k) + h_2(k)) \mod m </tex>, затем <tex> (h_1(k) + 2 \cdot h_2(k)) \mod m </tex> и так далее. В общем случае идёт проверка последовательности ячеек <tex> (h_1(k) + i \cdot h_2(k)) \mod m </tex> где <tex> i = (0, 1, \; ... \;, m - 1) </tex> |
+ | |||
+ | Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за <tex>O(1)</tex>, в худшем {{---}} за <tex>O(m)</tex>, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]]. | ||
+ | Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной. | ||
+ | |||
+ | <center> | ||
+ | <tex>\forall x \neq y \; \exists h_1,h_2 : \Pr(h_1(x)=h_1(y))> \Pr((h_1(x)=h_1(y))\cap(h_2(x)=h_2(y)))</tex> | ||
+ | </center> | ||
==Выбор хеш-функций== | ==Выбор хеш-функций== | ||
Строка 37: | Строка 44: | ||
==Простая реализация== | ==Простая реализация== | ||
− | + | Пусть у нас есть некоторый объект <tex> item </tex>, в котором определено поле <tex> key </tex>, от которого можно вычислить хеш-функции <tex> h_1(key)</tex> и <tex> h_2(key) </tex> | |
− | Так же у нас есть таблица <tex> table </tex> величиной <tex> m </tex> состоящая из объектов типа <tex> item </tex>. | + | Так же у нас есть таблица <tex> table </tex> величиной <tex> m </tex>, состоящая из объектов типа <tex> item </tex>. |
===Вставка=== | ===Вставка=== | ||
− | <pre>insert(item) | + | <pre>insert(item) |
x = h1(item.key) | x = h1(item.key) | ||
y = h2(item.key) | y = h2(item.key) | ||
− | for (i = 0; i < m; i++) | + | for (i = 0; i < m; i++) |
− | if | + | if table[x] == null |
table[x] = item | table[x] = item | ||
− | return | + | return |
− | + | x = (x + y) mod m | |
− | x = (x + y) mod m | + | table.resize() //ошибка, требуется увеличить размер таблицы</pre> |
− | |||
− | |||
− | |||
===Поиск=== | ===Поиск=== | ||
− | <pre>search(key) | + | <pre>search(key) |
x = h1(key) | x = h1(key) | ||
y = h2(key) | y = h2(key) | ||
− | for (i = 0; i < m; i++) | + | for (i = 0; i < m; i++) |
− | if | + | if table[x] != null |
− | if | + | if table[x].key == key |
return table[x] | return table[x] | ||
else | else | ||
return null | return null | ||
− | x = (x + y) mod m | + | x = (x + y) mod m |
− | + | return null</pre> | |
− | return null | ||
− | |||
==Реализация с удалением== | ==Реализация с удалением== | ||
Строка 74: | Строка 76: | ||
===Вставка=== | ===Вставка=== | ||
− | <pre>insert(item) | + | <pre>insert(item) |
x = h1(item.key) | x = h1(item.key) | ||
y = h2(item.key) | y = h2(item.key) | ||
− | for (i = 0; i < m; i++) | + | for (i = 0; i < m; i++) |
− | if | + | if table[x] == null || deleted[x] |
table[x] = item | table[x] = item | ||
deleted[x] = false | deleted[x] = false | ||
− | return | + | return |
− | + | x = (x + y) mod m | |
− | x = (x + y) mod m | + | table.resize() //ошибка, требуется увеличить размер таблицы</pre> |
− | |||
− | |||
− | |||
===Поиск=== | ===Поиск=== | ||
− | <pre>search(key) | + | <pre>search(key) |
x = h1(key) | x = h1(key) | ||
y = h2(key) | y = h2(key) | ||
− | for (i = 0; i < m; i++) | + | for (i = 0; i < m; i++) |
− | if | + | if table[x] != null |
− | if | + | if table[x].key == key && !deleted[x] |
return table[x] | return table[x] | ||
else | else | ||
return null | return null | ||
− | x = (x + y) mod m | + | x = (x + y) mod m |
− | + | return null</pre> | |
− | return null | ||
− | |||
===Удаление=== | ===Удаление=== | ||
− | <pre>remove(key) | + | <pre>remove(key) |
x = h1(key) | x = h1(key) | ||
y = h2(key) | y = h2(key) | ||
− | for (i = 0; i < m; i++) | + | for (i = 0; i < m; i++) |
− | if | + | if table[x] != null |
− | if | + | if table[x].key == key |
deleted[x] = true | deleted[x] = true | ||
else | else | ||
return | return | ||
− | x = (x + y) mod m | + | x = (x + y) mod m</pre> |
− | |||
− | |||
==См. также== | ==См. также== |
Версия 16:48, 7 мая 2012
Двойное хеширование — метод борьбы с коллизиями, возникающими при закрытом хешировании, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.
Содержание
Принцип двойного хеширования
При двойном хешировании используются две независимые хеш-функции
и . Пусть — это наш ключ, — размер нашей таблицы, — остаток от деления на , тогда сначала исследуется ячейка с адресом , если она уже занята, то рассматривается , затем и так далее. В общем случае идёт проверка последовательности ячеек гдеТаким образом, операции вставки, удаления и поиска в лучшем случае выполняются за линейного разрешения коллизий. Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.
, в худшем — за , что не отличается от обычного
Выбор хеш-функций
может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, должна возвращать значения:
- не равные
- независимые от
- взаимно простые с величиной хеш-таблицы
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а
возвращает натуральные числа, меньшие . Второй — размер таблицы является степенью двойки, а возвращает нечетные значения.Например, если размер таблицы равен
, то в качестве можно использовать функцию видаПример
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:
Мы хотим вставить ключ 14. Изначально
. Тогда . Но ячейка с индексом 1 занята, поэтому увеличиваем на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При получаем . Ячейка с номером 9 свободна, значит записываем туда наш ключ.Таким образом, основная особенность двойного хеширования состоит в том, что при различных
пара дает различные последовательности ячеек для исследования.Простая реализация
Пусть у нас есть некоторый объект
, в котором определено поле , от которого можно вычислить хеш-функции иТак же у нас есть таблица
величиной , состоящая из объектов типа .Вставка
insert(item) x = h1(item.key) y = h2(item.key) for (i = 0; i < m; i++) if table[x] == null table[x] = item return x = (x + y) mod m table.resize() //ошибка, требуется увеличить размер таблицы
Поиск
search(key) x = h1(key) y = h2(key) for (i = 0; i < m; i++) if table[x] != null if table[x].key == key return table[x] else return null x = (x + y) mod m return null
Реализация с удалением
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив
типов , равный по величине массиву . Теперь при удалении мы просто будем помечать наш объект как удалённый, а при добавлении как не удалённый и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.Вставка
insert(item) x = h1(item.key) y = h2(item.key) for (i = 0; i < m; i++) if table[x] == null || deleted[x] table[x] = item deleted[x] = false return x = (x + y) mod m table.resize() //ошибка, требуется увеличить размер таблицы
Поиск
search(key) x = h1(key) y = h2(key) for (i = 0; i < m; i++) if table[x] != null if table[x].key == key && !deleted[x] return table[x] else return null x = (x + y) mod m return null
Удаление
remove(key) x = h1(key) y = h2(key) for (i = 0; i < m; i++) if table[x] != null if table[x].key == key deleted[x] = true else return x = (x + y) mod m
См. также
Литература
- Бакнелл Дж. М. Фундаментальные алгоритмы и структуры данных в Delphi, 2003
- Кнут Д. Э. Искусство программирования, том 3. Сортировка и поиск, 2-е издание, 2000
- Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. Алгоритмы. Построение и анализ, 2010
- Седжвик Р. Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск, 2003