Идеальное хеширование — различия между версиями
Строка 1: | Строка 1: | ||
− | '''Двойное хеширование''' | + | '''Двойное хеширование''' {{---}} метод борьбы с возникающими коллизиями при [[Открытое_и_закрытое_хеширование#Закрытое хеширование|закрытом хешировании]], в котором хеш-таблица заполняется равномерней чем при [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейном разрешении коллизий]] коллизий, что способствует уменьшению размеров кластеров. |
==Принцип двойного хеширования== | ==Принцип двойного хеширования== | ||
− | При двойном хешировании используются две независимые хеш-функции <tex> h_1(k) </tex> и <tex> h_2(k) </tex>. | + | При двойном хешировании используются две независимые хеш-функции <tex> h_1(k) </tex> и <tex> h_2(k) </tex>. Пусть <tex> k </tex> это наш ключ, <tex> m </tex> размер нашей таблицы, <tex> \mod m </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> h_1 </tex> может быть обычной хеш-функцией. Однако | + | <tex> h_1 </tex> может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, <tex> h_2 </tex> должна возвращать значения: |
*не равные <tex> 0 </tex> | *не равные <tex> 0 </tex> | ||
*независимые от <tex> h_1 </tex> | *независимые от <tex> h_1 </tex> | ||
*взаимно простые с величиной хеш-таблицы | *взаимно простые с величиной хеш-таблицы | ||
− | Например, если размер таблицы равен <tex> m </tex>, то в качестве <tex> h_2 </tex> можно использовать функцию вида <tex> h_2(k) = k \ | + | Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а <tex> h_2 </tex> возвращает натуральные числа, меньшие <tex> m </tex>. Второй {{---}} размер таблицы является степенью двойки, а <tex> h_2 </tex> возвращает нечетные значения. |
+ | |||
+ | Например, если размер таблицы равен <tex> m </tex>, то в качестве <tex> h_2 </tex> можно использовать функцию вида <tex> h_2(k) = k \mod (m-1) + 1 </tex> | ||
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]] | [[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]] | ||
Строка 19: | Строка 21: | ||
<center> | <center> | ||
− | <tex> h(k,i) = (h_1(k) + i \cdot h_2(k)) \ | + | <tex> h(k,i) = (h_1(k) + i \cdot h_2(k)) \mod 13 </tex> |
</center> | </center> | ||
<center> | <center> | ||
− | <tex> h_1(k) = k \ | + | <tex> h_1(k) = k \mod 13 </tex> |
</center> | </center> | ||
<center> | <center> | ||
− | <tex> h_2(k) = 1 + k \ | + | <tex> h_2(k) = 1 + k \mod 11 </tex> |
</center> | </center> | ||
− | Мы хотим вставить ключ 14. Изначально <tex> i = 0 </tex>. Тогда <tex> h(14,0) = (h_1(14) + 0\cdot h_2(14)) \ | + | Мы хотим вставить ключ 14. Изначально <tex> i = 0 </tex>. Тогда <tex> h(14,0) = (h_1(14) + 0\cdot h_2(14)) \mod 13 = 1 </tex>. Но ячейка с индексом 1 занята, поэтому увеличиваем <tex> i </tex> на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При <tex> i = 2 </tex> получаем <tex> h(14,2) = (h_1(14) + 2\cdot h_2(14)) \mod 13 = 9 </tex>. Ячейка с номером 9 свободна, значит записываем туда наш ключ. |
− | |||
− | |||
Таким образом, основная особенность двойного хеширования состоит в том, что при различных <tex> k </tex> пара <tex> (h_1(k),h_2(k)) </tex> дает различные последовательности ячеек для исследования. | Таким образом, основная особенность двойного хеширования состоит в том, что при различных <tex> k </tex> пара <tex> (h_1(k),h_2(k)) </tex> дает различные последовательности ячеек для исследования. | ||
Строка 129: | Строка 129: | ||
==Ссылки== | ==Ссылки== | ||
− | * [http://en.wikipedia.org/wiki/Double_hashing Double_hashing] | + | * [http://en.wikipedia.org/wiki/Double_hashing Wikipedia: Double_hashing] |
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы] | * [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы] | ||
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием] | * [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием] |
Версия 21:34, 6 мая 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 } error() //ошибка, требуется увеличить размер таблицы }
Поиск
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 } error() //ошибка, требуется увеличить размер таблицы }
Поиск
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