Двойное хеширование

редактировать

Двойное хеширование - это метод компьютерного программирования, используемый вместе с открытой адресацией в хэш-таблицах для разрешения хэш-коллизий, используя вторичный хэш ключа в качестве смещения при возникновении коллизии. Двойное хеширование с открытой адресацией - это классическая структура данных в таблице T {\ displaystyle T}T .

Метод двойного хеширования использует одно хеш-значение в качестве индекса в таблице, а затем многократно продвигает интервал вперед до желаемого значения. обнаружено, достигнуто пустое место или произведен поиск по всей таблице; но этот интервал устанавливается второй независимой хэш-функцией . В отличие от альтернативных методов разрешения конфликтов линейного зондирования и квадратичного зондирования, интервал зависит от данных, поэтому значения, отображаемые в одно и то же место, имеют разные последовательности сегментов; это сводит к минимуму повторяющиеся коллизии и эффекты кластеризации.

Даны две случайные, однородные и независимые хэш-функции h 1 {\ displaystyle h_ {1}}h_ { 1} и h 2 {\ displaystyle h_ {2}}h_ {2} , i {\ displaystyle i}i -е место в последовательности сегмента для значения k {\ displaystyle k}k в хеш-таблице | Т | {\ displaystyle | T |}| T | buckets: h (i, k) = (h 1 (k) + i ⋅ h 2 (k)) mod | Т |. {\ displaystyle h (i, k) = (h_ {1} (k) + i \ cdot h_ {2} (k)) {\ bmod {|}} T |.}{\ displaystyle h (i, k) = (h_ {1} (k) + i \ cdot h_ {2} (k)) {\ bmod {|}} T |.} Как правило, h 1 {\ displaystyle h_ {1}}h_ { 1} и h 2 {\ displaystyle h_ {2}}h_ {2} выбираются из набора универсального хеша функции; h 1 {\ displaystyle h_ {1}}h_ { 1} выбран, чтобы иметь диапазон {0, | Т | - 1} {\ displaystyle \ {0, | T | -1 \}}{ \ displaystyle \ {0, | T | -1 \}} и h 2 {\ displaystyle h_ {2}}h_ {2} , чтобы иметь диапазон {1, | Т | - 1} {\ displaystyle \ {1, | T | -1 \}}{\ displaystyle \ {1, | T | -1 \}} . Двойное хеширование приближает случайное распределение; более точно, попарные независимые хеш-функции дают вероятность (n / | T |) 2 {\ displaystyle (n / | T |) ^ {2}}{\ displaystyle ( п / | T |) ^ {2}} , что любая пара ключей будет следовать той же последовательности сегментов.

Содержание
  • 1 Выбор h 2 (k)
  • 2 Анализ
  • 3 Расширенное двойное хеширование
  • 4 См. Также
  • 5 Ссылки
  • 6 Внешние ссылки
Выбор h 2 (k)

Вторичная хеш-функция h 2 (k) {\ displaystyle h_ {2} (k)}{\ displaystyle h_ { 2} (к)} должен иметь несколько характеристик:

  • он никогда не должен давать нулевой индекс
  • он должен циклически проходить через всю таблицу
  • он должен быть очень быстрым для вычисления
  • его должен быть попарно независимым от h 1 (k) {\ displaystyle h_ {1} (k)}{\ displaystyle h_ {1} (k)}
  • Характеристики распределения h 2 {\ displaystyle h_ {2}}h_ {2} неактуальны. Это аналогично генератору случайных чисел - необходимо только, чтобы h 2 {\ displaystyle h_ {2}}h_ {2} был "относительно простым" с | T |.

In На практике, если для обеих функций используется хеширование деления, делители выбираются как простые числа.

Анализ

Пусть n {\ displaystyle n}n будет количеством элементов, хранящихся в T {\ displaystyle T}T , тогда коэффициент нагрузки T {\ displaystyle T}T равен α = n / | Т | {\ Displaystyle \ альфа = n / | T |}{\ displaystyle \ alpha = n / | T |} . То есть начните с случайного, равномерного и независимого выбора двух универсальных хэш-функций функций h 1 {\ displaystyle h_ {1}}h_ { 1} и h 2 {\ displaystyle h_ {2}}h_ {2} для построения двойной хеш-таблицы T {\ displaystyle T}T . Все элементы помещаются в T {\ displaystyle T}T двойным хешированием с использованием h 1 {\ displaystyle h_ {1}}h_ { 1} и h 2 { \ Displaystyle h_ {2}}h_ {2} . Для ключа k {\ displaystyle k}k (i + 1) {\ displaystyle (i + 1)}(i + 1) -й хэш-адрес вычисляется с помощью :

h (i, k) = (h 1 (k) + i ⋅ h 2 (k)) mod | Т |. {\ displaystyle h (i, k) = (h_ {1} (k) + i \ cdot h_ {2} (k)) {\ bmod {|}} T |.}{\ displaystyle h (i, k) = (h_ {1} (k) + i \ cdot h_ {2} (k)) {\ bmod {|}} T |.}

Пусть T { \ displaystyle T}T с фиксированным коэффициентом нагрузки α: 1>α>0 {\ displaystyle \ alpha: 1>\ alpha>0}\alpha: 1>\ alpha>0 .

<12097>Брэдфорд и Катехакис показал ожидаемое количество зондов для неудачного поиска в T {\ displaystyle T}T , все еще используя эти изначально выбранные хэш-функции, составляет 1 1 - α {\ displaystyle {\ frac {1} {1- \ alpha}}}\ frac {1} {1- \ alpha} независимо от распределения входных данных. Достаточно попарной независимости хэш-функций.

Как и все другие формы открытых При адресации двойное хеширование становится линейным по мере приближения хеш-таблицы к максимальной емкости. Обычная эвристика заключается в ограничении загрузки таблицы до 75% емкости. В конце концов, потребуется повторное хеширование до большего размера, как и в случае со всеми другими схемами открытой адресации. с.

Расширенное двойное хеширование

В диссертации Питера Диллинджера указывается, что двойное хеширование создает нежелательные эквивалентные хеш-функции, когда хеш-функции обрабатываются как набор, как в фильтрах Блума : Если h 2 (y) = - h 2 (x) {\ displaystyle h_ {2} (y) = - h_ {2} (x)}{\ displaystyle h_ {2} (y) = - h_ {2} (x)} и h 1 (y) знак равно час 1 (Икс) + К ⋅ час 2 (Икс) {\ Displaystyle H_ {1} (Y) = H_ {1} (X) + К \ CDOT H_ {2} (X)}{\ displaystyle h_ {1} (y) = h_ {1} (x) + k \ cdot h_ {2} (x)} , затем h (i, y) = h (k - i, x) {\ displaystyle h (i, y) = h (ki, x)}{\ displaystyle h (i, y) = h (ki, x)} и наборы хешей {ч (0, х),..., h (k, x)} = {h (0, y),... час (к, y)} {\ displaystyle \ left \ {h (0, x),..., час (k, x) \ right \} = \ left \ {h (0, y),..., h (k, y) \ right \}}{\ displaystyle \ left \ {h (0, x),..., h (k, x) \ right \} = \ left \ {h (0, y),..., h (k, y) \ right \}} идентичны. Это увеличивает вероятность столкновения в два раза по сравнению с ожидаемым 1 / | Т | 2 {\ displaystyle 1 / | T | ^ {2}}{\ displaystyle 1 / | T | ^ {2}} .

Кроме того, имеется значительное количество часто перекрывающихся хэш-наборов; если час 2 (y) = час 2 (x) {\ displaystyle h_ {2} (y) = h_ {2} (x)}{\ displaystyle h_ {2} (y) = h_ {2 } (x)} и h 1 (y) = час 1 (x) ± h 2 (x) {\ displaystyle h1 (y) = h1 (x) \ pm h_ {2} (x)}{\ displaystyle h1 (y) = h1 (x) \ pm h_ {2} (x)} , затем h (i, y) = h (i ± 1, x) {\ displaystyle h (i, y) = h (i \ pm 1, x)}{\ displaystyle h (i, y) = h ( я \ pm 1, x)} и сравнение дополнительных значений хеш-функции (расширение диапазона i {\ displaystyle i}i ) бесполезен.

Добавление квадратичного члена i 2, {\ displaystyle i ^ {2},}{\ displaystyle i ^ {2},} i (i + 1) / 2 {\ displaystyle i (i + 1) / 2}{\ displaystyle i (i + 1) / 2} (a треугольное число ) или даже i 2 ⋅ h 3 (x) {\ displaystyle i ^ {2} \ cdot h_ {3} (x)}{\ displaystyle i ^ {2} \ cdot h_ {3} (x)} (тройное хеширование ) к хеш-функции несколько улучшает хеш-функцию, но не решает эту проблему; если:

час 1 (y) = час 1 (x) + k ⋅ час 2 (x) + k 2 ⋅ час 3 (x), {\ displaystyle h_ {1} (y) = h_ {1} ( x) + k \ cdot h_ {2} (x) + k ^ {2} \ cdot h_ {3} (x),}{\ displaystyle h_ {1} (y) = h_ {1} (x) + k \ cdot h_ {2} (x) + k ^ {2} \ cdot h_ {3} (x),}
h 2 (y) = - h 2 (x) - 2 k ⋅ h 3 (х), {\ displaystyle h_ {2} (y) = - h_ {2} (x) -2k \ cdot h_ {3} (x),}{\ displaystyle h_ {2} (y) = - h_ {2} (x) -2k \ cdot h_ {3} (x),} и
h 3 ( у) = h 3 (х). {\ displaystyle h_ {3} (y) = h_ {3} (x).}{\ displaystyle h_ {3} (y) = h_ { 3} (х).}

, тогда

h (k - i, y) = h 1 (y) + (k - i) ⋅ h 2 (y) + (k - i) 2 ⋅ h 3 (y) = h 1 (y) + (k - i) (- h 2 (x) - 2 kh 3 (x)) + (k - i) 2 h 3 (x) = h 1 (y) + (i - k) h 2 (x) + (2 ki - 2 k 2) h 3 (x) + (k 2 - 2 ki + i 2) h 3 ( х) = h 1 (y) + (i - k) h 2 (x) + (i 2 - k 2) h 3 (x) = h 1 (x) + kh 2 (x) + k 2 h 3 ( x) + (i - k) h 2 (x) + (i 2 - k 2) h 3 (x) = h 1 (x) + ih 2 (x) + i 2 h 3 (x) = h (i, Икс). {\ Displaystyle {\ begin {выровнен} ч (ки, у) = ч_ {1} (у) + (ки) \ cdot h_ {2} (у) + (ки) ^ {2} \ cdot h_ {3 } (y) \\ = h_ {1} (y) + (ki) (- h_ {2} (x) -2kh_ {3} (x)) + (ki) ^ {2} h_ {3} ( x) \\ = h_ {1} (y) + (ik) h_ {2} (x) + (2ki-2k ^ {2}) h_ {3} (x) + (k ^ {2} -2ki + i ^ {2}) h_ {3} (x) \\ = h_ {1} (y) + (ik) h_ {2} (x) + (i ^ {2} -k ^ {2}) h_ {3} (x) \\ = h_ {1} (x) + kh_ {2} (x) + k ^ {2} h_ {3} (x) + (ik) h_ {2} (x) + (i ^ {2} -k ^ {2}) h_ {3} (x) \\ = h_ {1} (x) + ih_ {2} (x) + i ^ {2} h_ {3} (x) \\ = h (i, x). \\\ end {align}}}{\ displaystyle {\ begin {align} h ( ki, y) = h_ {1} (y) + (ki) \ cdot h_ {2} (y) + (ki) ^ {2} \ cdot h_ {3} (y) \\ = h_ {1 } (y) + (ki) (- h_ {2} (x) -2kh_ {3} (x)) + (ki) ^ {2} h_ {3} (x) \\ = h_ {1} ( y) + (ik) h_ {2} (x) + (2ki-2k ^ {2}) h_ {3} (x) + (k ^ {2} -2ki + i ^ {2}) h_ {3} (x) \\ = h_ {1} (y) + (ik) h_ {2} (x) + (i ^ {2} -k ^ {2}) h_ {3} (x) \\ = h_ {1} (x) + kh_ {2} (x) + k ^ {2} h_ {3} (x) + (ik) h_ {2} (x) + (i ^ {2} -k ^ { 2}) h_ {3} (x) \\ = h_ {1} (x) + ih_ {2} (x) + i ^ {2} h_ {3} (x) \\ = h ( я, х). \\\ конец {выровнен}}}

Добавление кубического члена i 3 {\ displaystyle i ^ {3}}{\ displaystyle i ^ {3}} или (i 3 - i) / 6 {\ displaystyle (i ^ {3} -i) / 6}{\ displaystyle (i ^ {3} -i) / 6} (a тетраэдрическое число ), действительно решает проблему, метод, известный как расширенное двойное хеширование . Это может быть эффективно вычислено с помощью прямого дифференцирования :

структурного ключа; // Непрозрачный extern unsigned int h1 (struct key const *), div class="ht" (struct key const *); // Вычислить k хеш-значений из двух базовых хеш-функций // h1 () и div class="ht" () с использованием расширенного двойного хеширования. При возврате // hashes [i] = h1 (x) + i * div class="ht" (x) + (i * i * i - i) / 6 // Использует преимущества автоматической упаковки (модульное сокращение) // беззнаковых типов в C. void hash (struct key const * x, unsigned int hashes, unsigned int n) {unsigned int a = h1 (x), b = div class="ht" (x), я; for (i = 0; i < n; i++) { hashes[i] = a; a += b; // Add quadratic difference to get cubic b += i; // Add linear difference to get quadratic // i++ adds constant difference to get linear } }
См. также
Ссылки
Внешние ссылки
Последняя правка сделана 2021-05-18 14:05:30
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте