Základy algoritmizace Hašování
Problematika hašování Hašování - nástroj na jednoduchý způsob "zakódování vstupních dat. Vstupní data jsou zpracována hašovací funkcí jsou jistým způsobem komprimována. Relativně malý výstup, který vystihuje podstatu zpracovaných dat.
Použití hašování Použití - hašovací tabulky (abstraktní datová struktura) - komunikační a bezpečnostní protokoly - zpětné rychlé vyhledávání z velkého množství uložených dat - signatury
Definice hashování Mějme nějakou množinu U, nazýváme jí univerzum. Prvky z toho univerza nazváme klíče. Hašovací funkce h(k):u (0,...,m), kde m N je rozsah klíčů U > m => některé dva prvky se nutně musí hašovat na stejnou hodnotu - kolize.
ADT - tabulka jeprázdná(t) - vrací boolean (pravda v případě, že tabulka T neobsahuje žádný prvek vlož(t, p) - výsledkem je tabulka T rozšířená o prvek p vyber(t, k) - výsledkem operace je tabulka T, u které v případě, že obsahovala prvek p s klíčem k je tento prvek zrušen, jinak zůstává nezměněna, jevtabulce(t, k) - vrací boolean (pravda v případě, že tabulka T obsahuje prvek p s klíčem k, jinak nepravda)
Princip hašování Pojmem hash-funkce h budeme rozumět aritmetickou transformaci vstupních dat k (klíčů) do tvaru h(k), kterému budeme říkat hash hodnota. Hlavním účelem hashovací funkce je transformace klíčů univerza U do pozic (slotů) hashovací tabulky. Zmenší se nároky na paměť - pro původních U klíčů stačí udržovat jen m hodnot.
Princip hašovacích tabulek Hashovací funkce h zobrazuje univerzum klíčů U na sloty hašovací tabulky T[0,..., m-1] h: U {0,1,,m-1} Říkáme, že prvek s klíčem k je hašován do slotu h(k) nebo také, že h(k) je hašovací hodnota klíče k. Většinou zobrazujeme pouze S U.
Hašování U - univerzum k x k x k x k 1 k 4 h(k 1 ) h(k 3 ) = h(k 4 ) kolize! S U k 3 k 2 h(k 2 ) k x S - počet hashovaných klíčů
Řešení kolizí snaha volit hašovací funkci tak, aby docházelo k minimálnímu počtu kolizí dále tedy předpokládejme hašovací funkci ve tvaru h(k)= k mod m, kde m je prvočíslo dva základní způsoby řešení kolizí - separátní řetězení - otevřená adresace
Volba hash funkce (metoda dělení) h(k) = k mod m nevhodné hodnoty m: mocniny 2, mocniny 10 vhodné hodnoty m : prvočísla vzdálená od mocnin 2 Příklad: m=701 h(k) = k mod 701
Separátní řetězení (Separate Chaining) Klíče, které hashují na stejný index, jsou uloženy v seznamu. Problém kolizí se převádí na práci se seznamem.
Separátní řetězení h(k) = k mod m kde m = 11 h() = mod 11 = 6 0 1 2 3 4 5 6 7 8 9 10
Separátní řetězení h(k) = k mod m Kde m = 11 79 h(79) = 79 mod 11 = 2 0 1 2 3 4 5 6 7 8 9 10 79
Separátní řetězení h(k) = k mod m kde m = 11, 79 69 h(69) = 69 mod 11 = 3 0 1 2 3 4 5 6 7 8 9 10 79 69
Separátní řetězení 0 h(k) = k mod m kde m = 11 1 2 3 79 69, 79, 69 72 h(72) = 72 mod 11 = 6 kolize 4 5 6 7 72 8 9 10
Separátní řetězení 0 h(k) = k mod m kde m = 11, 79, 69, 72, 24, 14, 20, 33, 85, 52 h(24) = 2 h(14) = 3 h(20) = 9 h(33) = 0 h(85) = 8 h(52) = 4 1 2 3 4 5 6 7 8 9 10 79 69 24 72
Separátní řetězení 0 h(k) = k mod M kde M = 11, 79, 69, 72, 24, 14, 20, 33, 85, 52 h(24) = 2 h(14) = 3 h(20) = 9 h(33) = 0 h(85) = 8 h(52) = 4 1 2 3 4 5 6 7 8 9 10 33 79 69 52 85 20 24 14 72
Separátní řetězení void Insert(Item item) { Table[h(item.key)].InsertToList(item); } bool Search(Key k) { return Table[h(k)].SearchInList(k); } void Delete(Item item) { Table[h(item.key)].DeleteFromList(item); }
Separátní řetězení Nechť je dána hashovací tabulka T s m sloty, ve které je uloženo n prvků. Číslo, kde = n/m se nazývá faktor naplnění hašovací tabulky, udává zároveň i průměrnou délku seznamu ve slotu. Efektivita pro nejhorší případ: všech n klíčů se hashuje do jednoho slotu a vytváří tak seznam délky n. Složitost pro nejhorší případ je tedy O(n) plus čas nutný pro výpočet hašovací funkce. V průměrném případě pro vyhledání potřebujeme O(1).
Otevřená adresace Předpokládejme, že m>n, jinými slovy, tabulka je větší než potřebujeme. Klíče můžeme uložit tabulce na prázdná místa a vyřešit tak kolize. Pro tento případ se používají metody otevřené adresace. - lineární pokusy (Linear Probing) - dvojité hašování (Double Hashing)
Lineární pokusy Při vzniku kolize procházíme následující prvky tabulky a prvek umístíme do prvního volného místa. Dochází zde ovšem ke zhlukování prvků s klíči, které se hašují na stejný index. Rozšířená hašovací funkce h(k, i):uxn (0,...,m), kde i N je číslo pokusu
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11 h() = mod 11 = 6 0 1 2 3 4 5 6 7 8 9 10
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11 79 h(79) = 79 mod 11 = 2 0 1 2 3 4 5 6 7 8 9 10 79
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11, 79 69 h(69) = 69 mod 11 = 3 0 1 2 3 4 5 6 7 8 9 10 79 69
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11, 79, 69 72 h(72) = 72 mod 11 = 6 zjistíme další slot kolize 0 1 2 3 4 5 6 7 8 9 10 79 69 72
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11, 79, 69, 72 24 h(24,0) = 24 mod 11 = 2 h(24,1) = 2 + 1 = 3 h(24,2) = 2 + 2 = 4 zjistíme další slot kolize 0 1 2 3 4 5 6 7 8 9 10 79 69 24 72
Lineární pokusy h(k) = k mod m h(k,i) = (h (k) + i) mod m kde m = 11, 79, 69, 72, 24, 14, 20, 33, 85, 52 h(14) = 3 h(20) = 9 h(33) = 0 h(85) = 8 h(52) = 4 0 1 2 3 4 5 6 7 8 9 10 33 79 69 24 14 72 85 20 52
Lineární pokusy void HashInsert(Item item) { int j, i = 0; do { j = h(item.key, i); if (T[j] == null) {// nalezen volný slot T[j] = item; return; } // if else i += 1; } while (i < m); error "overflow" } // určení dalšího pokusu
Lineární pokusy bool HashSearch(Key k) { int j, i = 0; do { j = h(k, i); // určení dalšího pokusu if (T[j] == k) return true; i += 1; } while ((T[j]!= null) && (i!= m)); return false; }
Lineární pokusy Nejhorší případ nastane, když hodnota hašovací funkce je stejná pro všechny prvky. Potom je složitost vyhledání prvku v tabulce O(n). Průměrně jde o algoritmus se složitostí O(1).
Dvojité hašování Dvojité hashování je patrně nejlepší metodou při otevřeném adresování, protože permutace slotů poskytované touto metodou mají nejblíže k náhodně voleným permutacím. Dvojité hashování používá hashovací funkci tvaru: h(k,i) = (h1(k) + ih2(k)) mod m kde h1 a h2 jsou pomocné hashovací funkce.
Dvojité hašování Na počátku je testován slot T[h1(k)]. Další pokusy jsou určeny posunem o pozic modulo m. h2(k) Je tak umožněn větší rozptyl pro výběr sekvencí pokusů, protože na klíči k závisí nejen počáteční pozice, ale i velikost kroku, o který se v tabulce posunujeme.
Dvojité hašování Implementace algoritmu bude obdobná jako implementace lineárních pokusů s tím, že na rozdíl od metody lineárních pokusů se v případě kolize neposuneme o jeden prvek, ale vypočítáme posun takto: inc = m-2- (k mod (m-2))
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9) h() = mod 11 = 6 0 1 2 3 4 5 6 7 8 9 10
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9) 79 h(79) = 79 mod 11 = 2 0 1 2 3 4 5 6 7 8 9 10 79
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9), 79 69 h(69) = 69 mod 11 = 3 0 1 2 3 4 5 6 7 8 9 10 79 69
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9), 79, 69 72 h(72) = 72 mod 11 = 6 kolize zjistíme hodnotu inc pro obsazení dalšího slotu inc = 9 - (72 mod 9) = 9-0 = 9 0 1 2 3 4 5 6 7 8 9 10 79 69 72
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9), 79, 69, 72 24 h(24) = 24 mod 11 = 2 kolize zjistíme hodnotu inc pro obsazení dalšího slotu inc = 9 - (24 mod 9) = 9-6 = 3 0 1 2 3 4 5 6 7 8 9 10 79 69 72 24
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9), 79, 69, 72, 24, 14, 20, 33, 85, 52 h(14) = 3 h(20) = 9 h(33) = 0 h(85) = 8 h(52) = 4 inc(14) = 9-5 = 4 inc(52) = 9-7 = 2 0 1 2 3 4 5 6 7 8 9 10 33 79 69 72 24 14 85 20
Dvojité hašování h(k) = k mod M kde M = 11 inc = M-2- (k mod (M-2)) inc = 9 - (k mod 9), 79, 69, 72, 24, 14, 20, 33, 85, 52 h(14) = 3 h(20) = 9 h(33) = 0 h(85) = 8 h(52) = 4 inc(14) = 9-5 = 4 inc(52) = 9-7 = 2 0 1 2 3 4 5 6 7 8 9 10 33 79 69 72 24 14 85 20 52
Nejvyšší počty pokusů při neúspěšném vyhledavání jako funkce faktoru naplnění α
Nejvyšší počty pokusů při neúspěšném vyhledavání jako funkce faktoru naplnění α