Идеальное хеширование — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(добавлен псевдокод)
Строка 1: Строка 1:
'''Двойное хеширование (double hashing)''' - один из методов закрытого хеширования. Перебор ячеек хеш-таблицы, возникающий при двойном хешировании, обладает свойствами, присущими равномерному хешированию. При двойном хешировании хеш-функция <tex> h </tex> имеет следующий вид:
+
'''Двойное хеширование''' – метод борьбы с возникающими коллизиями при [[Открытое_и_закрытое_хеширование#Закрытое хеширование|закрытом хешировании]], в котором хеш-таблица заполняется равномерней чем при [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейном разрешении коллизий]] коллизий, что способствует уменьшению размеров кластеров.
  
<center>
+
==Принцип двойного хеширования==
<tex> h(k,i) = (h_1(k) + i\cdot h_2(k)) \; mod \; m </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>
</center>
+
 
 +
==Выбор хеш-функций==
 +
<tex> h_1 </tex> может быть обычной хеш-функцией. Однако что бы в случаях коллизий была проверенна вся хеш-таблица, <tex> h_2 </tex> должна возвращать значения:
 +
*не равные <tex> 0 </tex>
 +
*независимые  от <tex> h_1 </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|Вставка при двойном хешировании]]
  
где <tex> h_1 </tex> и <tex> h_2 </tex> - вспомогательные хеш-функции, <tex> m </tex> - размер хеш-таблицы. Иными словами, последовательность индексов исследуемых ячеек при работе с ключом <tex> k </tex> представляет собой арифметическую прогрессию (по модулю <tex> m </tex>) с первым членом <tex> h_1(k) </tex> и шагом <tex> h_2(k) </tex>. Следовательно, в данном случае последовательность исследования зависит от ключа k по двум параметрам - выбор начальной исследуемой ячейки и расстояние между двумя исследуемыми ячейками, так как оба параметра зависят от значения ключа.
+
==Пример==
  
Пример вставки элемента при двойном хешировании приведен на рисунке справа.
+
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:
  
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:
+
<center>
 +
<tex> h(k,i) = (h_1(k) + i \cdot h_2(k)) \; mod \; 13 </tex>
 +
</center>
  
 
<center>
 
<center>
Строка 28: Строка 37:
  
 
==Простая реализация==
 
==Простая реализация==
 +
Пускай у нас есть некоторый объект <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>.
 +
 
===Вставка===
 
===Вставка===
 
<pre>insert(item){
 
<pre>insert(item){
Строка 37: Строка 50:
 
         return
 
         return
 
       }
 
       }
       x = (x + y) % m
+
       x = (x + y) mod m
 
   }
 
   }
 
   error() //ошибка, требуется увеличить размер таблицы
 
   error() //ошибка, требуется увеличить размер таблицы
Строка 52: Строка 65:
 
       else
 
       else
 
         return null
 
         return null
       x = (x + y) % m
+
       x = (x + y) mod m
 
   }
 
   }
 
   return null
 
   return null
Строка 58: Строка 71:
  
 
==Реализация с удалением==
 
==Реализация с удалением==
 +
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив <tex>deleted</tex> типов <tex>bool</tex>, равный по величине массиву <tex>table</tex>. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.
 +
 
===Вставка===
 
===Вставка===
 
<pre>insert(item){
 
<pre>insert(item){
Строка 68: Строка 83:
 
         return
 
         return
 
       }
 
       }
       x = (x + y) % m
+
       x = (x + y) mod m
 
   }
 
   }
 
   error() //ошибка, требуется увеличить размер таблицы
 
   error() //ошибка, требуется увеличить размер таблицы
Строка 83: Строка 98:
 
       else
 
       else
 
         return null
 
         return null
       x = (x + y) % m
+
       x = (x + y) mod m
 
   }
 
   }
 
   return null
 
   return null
Строка 98: Строка 113:
 
       else  
 
       else  
 
         return
 
         return
       x = (x + y) % m
+
       x = (x + y) mod m
 
   }
 
   }
 
}</pre>
 
}</pre>

Версия 21:30, 5 мая 2012

Двойное хеширование – метод борьбы с возникающими коллизиями при закрытом хешировании, в котором хеш-таблица заполняется равномерней чем при линейном разрешении коллизий коллизий, что способствует уменьшению размеров кластеров.

Принцип двойного хеширования

При двойном хешировании используются две независимые хеш-функции [math] h_1(k) [/math] и [math] h_2(k) [/math]. Пускай [math] k [/math] это наш ключ, [math] m [/math] размер нашей таблицы, [math] mod \; m [/math] это остаток от деления на [math] m [/math], тогда сначала исследуется ячейка с адресом [math] h_1(k) [/math], если она уже занята рассматривается [math] (h_1(k) + h_2(k)) \; mod \; m [/math], затем [math] (h_1(k) + 2 \cdot h_2(k)) \; mod \; m [/math] и т.д. В общем случае идёт проверка последовательности ячеек [math] (h_1(k) + i \cdot h_2(k)) \; mod \; m [/math] где [math] i = (0, 1, \; ... \;, m - 1) [/math]

Выбор хеш-функций

[math] h_1 [/math] может быть обычной хеш-функцией. Однако что бы в случаях коллизий была проверенна вся хеш-таблица, [math] h_2 [/math] должна возвращать значения:

  • не равные [math] 0 [/math]
  • независимые от [math] h_1 [/math]
  • взаимно простые с величиной хеш-таблицы

Например, если размер таблицы равен [math] m [/math], то в качестве [math] h_2 [/math] можно использовать функцию вида [math] h_2(k) = k \; mod \; (m-1) + 1 [/math]

Вставка при двойном хешировании

Пример

Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:

[math] h(k,i) = (h_1(k) + i \cdot h_2(k)) \; mod \; 13 [/math]

[math] h_1(k) = k \; mod \; 13 [/math]

[math] h_2(k) = 1 + k \; mod \; 11 [/math]

Мы хотим вставить ключ 14. Изначально [math] i = 0 [/math]. Тогда [math] h(14,0) = (h_1(14) + 0\cdot h_2(14)) \; mod \; 13 = 1 [/math]. Но ячейка с индексом 1 занята, поэтому увеличиваем [math] i [/math] на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При [math] i = 2 [/math] получаем [math] h(14,2) = (h_1(14) + 2\cdot h_2(14)) \; mod \; 13 = 9 [/math]. Ячейка с номером 9 свободна, значит записываем туда наш ключ.

Для того, чтобы последовательность исследования могла охватить всю таблицу, значение [math] h_2 [/math] должно быть взаимно простым с размером таблицы. Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а [math] h_2 [/math] возвращает натуральные числа, меньшие [math] m [/math]. Второй - размер таблицы является степенью двойки, а [math] h_2 [/math] возвращает нечетные значения.

Таким образом, основная особенность двойного хеширования состоит в том, что при различных [math] k [/math] пара [math] (h_1(k),h_2(k)) [/math] дает различные последовательности ячеек для исследования.

Простая реализация

Пускай у нас есть некоторый объект [math] item [/math], в котором определено поле [math] key [/math], от которого можно вычислить хеш-функции [math] h_1(key)[/math] и [math] h_2(key) [/math]

Так же у нас есть таблица [math] table [/math] величиной [math] m [/math] состоящая из объектов типа [math] item [/math].

Вставка

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
}

Реализация с удалением

Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив [math]deleted[/math] типов [math]bool[/math], равный по величине массиву [math]table[/math]. Теперь при удалении мы просто будем помечать наш объект как удалённый, а при добавлении как не удалённый и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.

Вставка

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

Ссылки