12. Речници - втора част

Речник АТД
Хеш таблици
Клетъчни масиви (bucket arrays)
Хеш функции
Хеш кодове
Компресиращи изображения
Схеми за решаване на колизии
Реализация на хеш-таблица със C++


Речник АТД

Function
Input
Output
Description
size()
-
Integer
Връща броя на елементите на D.
isEmpty()
-
Boolean
Проверява дали D е празен.
elements()
-
Iterator of objects (elements)
Връща елементите, записани в D.
keys()
-
Iterator of objects (keys) Връща ключовете, записани в D.
find(k)
Object (key) Position
Ако D съдържа член с ключ, равен на k, връща позицията на този член. Ако не, връща nullposition .
findAll(k)
Object (key) Iterator of Positions Връща итератор от позициите на всички членове, чийто ключове са равни на k.
insertItem(k,e)
Objects k (key) and e (element) -
Вмъква двойка с елемент e и ключ k в D.
removeElement(k)
Object (key)
-
Премахва член с ключ равен на k от D. Ако в D няма такъв член се генерира грешка (error condition).
removeAllElements(k)
Object (key) -
Премахва всички двойки с ключове равни на k от D.


Хеш таблици

Клетъчни масиви (bucket arrays)
Анализ на структурата клетъчен масив

Пример: Речник, състоящ се от (FN, name) - факултетен номер и име на студент, като речникът съхранява данни за студентите от този курс  (n < 100, N = 100000).


Хеш функции


  • Да се направи хеш таблица за речник, съдържащ двойки (SSN, Name), където SSN (social security number) е 9-цифрово положително число.
  • Ако хеш таблицата използва масив с размер N = 10000 и хеш функция h(x) = x mod 10000, т.е. (последните 4 цифри на x), може да възникнат колизии.
  • За да се избегнат колизии, ще трябва да се вземе N = 109 и хеш функция h(x) = x (Недостатък 1).


Хеш кодове

Хеш кодове в C++
C++ примери
32-bit integer if we have 32-bit integer hash function
int hashCode(int x)
{ return x; }
64-bit integer if we have 32-bit integer hash function
int hashCode(long x)
{ typedef unsigned long ulong;
return hashCode(static_cast<int>(static_cast<ulong>(x) >> 32)
+ static_cast<int>(x));
}
Полиномен хеш код
Циклични с преместване хеш кодове
int hashCode(const char* p, int len) // hash a character array
{ unsigned int h = 0;
for (int i = 0; i < len; i++)
{ h = (h << 5)|(h >> 27); // 5-bit cyclic shift
h += (unsigned int)p[i]; // add in next character
}
return hashCode(int(h));
}
Експериментални резултати за 25000 английски думи
Shift
Collisions Total
Collisions Max
0
23739
86
1
10517
21
5
4
2
6
6
2
11
453
4

Хеширане на типове с плаваща точка
int hashCode(const double& x)       // hash a double
{ int len = sizeof(x);
const char* p = reinterpret_cast<const char *>(&x);
return hashCode(p, len);
}
hash_code.cpp


Компресиращи изображения

Метод на деленето
Метод MAD
Умножи, събери и раздели (Multiply, Add and Divide - MAD):


Схеми за решаване на колизии

Самостоятелни вериги (separate chaining)


Отворено адресиране

При колизия обектът се поставя в друга клетка на таблицата.

Линейно пробване (linear probing)

  • h(x) = x mod 13
  • Добавяме ключове 18, 41, 22, 44, 59, 32, 31, 73 в този ред:
  • h(18) = 18 mod 13 = 5 --> a[5] = 18
    h(41) = 41 mod 13 = 2 --> a[2] = 41
    h(22) = 22 mod 13 = 9 --> a[9] = 22
    h(44) = 44 mod 13 = 5 --> a[5] заета, a[6] = 44;
    h(59) = 59 mid 13 = 7 --> a[7] = 59
    h(32) = 32  mod 13 = 6 --> a[6] заета, a[7]  заета, a[8] = 32
    h(31) = 31 mod 13 = 5 --> a[5]-a[9] заети, a[10] = 31
    h(73) = 73 mod 13 = 8 --> a[8]-a[10] заети, a[11] = 73

find(22), 22 mod 13 = 9, a[9] = 22 --> намерен ключ 22
find(23), 23 mod 13 = 10, a[10]  = 31, a[11] = 73, a[12] празно -> няма ключ 23
find(4), 4 mod 13 = 4, a[4] празно --> няма ключ 4
find(41), 41 mod 13 = 2, a[2] = 41 --> намерен ключ 41
find(2), 2 mod 13 = 2, a[2] = 41, a[3] празно --> няма ключ 2

Търсене с линейно пробване
  • Нека хеш таблицата A използва линейно пробване.
  • find(k)
    • Започва се с клетка h(k).
    • Пробват се последователни клетки, докато се появи един от следните случаи:
      • обект с ключ k е намерен, или
      • достигната е празна клетка, или
      • N клетки се пробвани без резултат.
Algorithm find(k)
   i h(k)
   p ← 0
   repeat
      cA[i]
      if c = ∅
          return Position(null)
      else if c.key() = k
          return Position(c)
      else
         i ← (i + 1) mod N
         pp + 1
   until p = N
   return Position(null)

Обновяване с линейно пробване

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

             (h(k)+ jd(k)) mod N за j = 0, 1, … , N −1.


  • Разглеждаме хеш таблица за съхранение на целочислени ключове, обработваща колизии с двойно хеширане
    • N = 13
    • h(k) = k mod 13
    • d(k) = 7 − k mod 7
  • Въвеждат се ключове 18, 41, 22, 44, 59, 32, 31, 73, в този ред:
  • h(18) = 18 mod 13 = 5 --> a[5] = 18, (j = 0)
    h(41) =  41 mod 13 = 2 --> a[2] = 41, (j = 0)
    h(22) = 22 mod 13 = 9 --> a[9] = 22, (j = 0)
    h(44) = 44 mod 13 = 5 --> a[5] ,(j = 0), заета, 

           d(44) = 7 - 44 mod 7 = 7 - 2 = 5 -- > 5 + 1.5 = 10, a[10] = 44, (j = 1)
    h(59) = 59 mod 13 = 7 --> a[7] = 59, (j = 0)
  • h(32) = 32  mod 13 = 6 --> a[6] = 32, (j = 0)
    h(31) = 31 mod 13 = 5 --> a[5] (j = 0), заетa, 

         d(31) = 7 - 31 mod 7 = 7 - 3 = 4 --> 5 + 1.4 = 9, a[9] (j = 1), заета,
                             5 + 2.4 = 13, a[0] = 31, (j = 2);
    h(73) = 73 mod 13 = 8 --> a[8] = 73, (j = 0)



Реализация на хеш-таблица със C++

html-8.1 (HashEntry)
html-8.2
(Position)
html-8.3
(Hash1)
html-8.
4 (Hash2)

hash.cpp