Datové struktury a algoritmy Část 11 Rozptylování - Hashing Petr Felkel
Slovník - Dictionary Řada aplikací potřebuje dynamickou množinu s operacemi: Search, Insert, Delete = slovník Př. Tabulka symbolů překladače identifikátor typ adresa suma int 0xFFFFDC09 ... ... ... DSA
Dictionary Many applications need dynamic set with operations: Search, Insert, Delete = dictionary Ex. Compiler symbol table identifier type address sum int 0xFFFFDC09 ... ... ... DSA
Vyhledávání Porovnáváním klíčů (log n) klíč prvku = hledaný klíč např. sekvenční vyhledávání,BVS,... Indexováním klíčem (přímý přístup) (1) klíč je přímo indexem rozsah klíčů ~ rozsahu indexů Rozptylováním průměrně (1)! výpočtem adresy z hodnoty klíče DSA
Searching Comparing search keys (log n) search key with keys in items e.g. sequential search,BST,... Key-indexed search (direct access) (1) use key value directly as array index key range ~ table indices Hashing expected (1)! transform keys into a table address DSA
Rozptylování - Hashing = kompromis mezi rychlostí a spotřebou paměti ∞ času - sekvenční vyhledávání ∞ paměti - přímý přístup (indexování klíčem) velikost tabulky reguluje čas vyhledání DSA
Hashing = time-space tradeoff ∞ time - sequential search ∞ space - key-indexed search (direct access) balance by adjusting hash-table size DSA
Rozptylování - Hashing Konstantní očekávaný čas pro vyhledání a vkládání (search and insert) !!! Něco za něco: čas provádění ~ délce klíče není vhodné pro operace výběru a řazení (select and sort) DSA
Hashing Constant expected time for search and insert but running time does depend on the length of the key not efficient for select and sort DSA
Rozptylování Dvě fáze 1. Výpočet rozptylovací funkce h(k) klíče 0 10 1 21 2 2 ... M-1 h(10) 10 h(21) 2 h(2) 21 h(31)? 31 h(k) Dvě fáze 1. Výpočet rozptylovací funkce h(k) 2. Vyřešení kolizí h(31) ..... kolize - index 1 již obsazen DSA
Hashing Two steps 1. Computation of the hash function h(k) klíče 0 10 1 21 2 2 ... M-1 h(10) 10 h(21) 2 h(2) 21 h(31)? 31 h(k) Two steps 1. Computation of the hash function h(k) 2. Handling of collisions h(31) ..... collision - index 1 already used DSA
1. Výpočet rozptylovací funkce h(k) Computation of the hash function h(k) DSA
Rozptylovací funkce h(k) Zobrazuje množinu klíčů K do intervalu adres A = <amin, amax>, obvykle <0,M-1> |K| >> |A| Synonyma: k1 k2, h(k1) = h(k2) = kolize DSA
Hash function h(k) Transforms the search key into a table address set of keys K table addresses A <amin, amax>, usually A <0,M-1> |K| >> |A| Synonyms: k1 k2, h(k1) = h(k2) = collision DSA
Rozptylovací funkce h(k) Je silně závislá na vlastnostech klíčů a jejich reprezentaci v paměti Ideálně: výpočetně co nejjednodušší (rychlá) aproximuje náhodnou funkci využije rovnoměrně adresní prostor generuje minimum kolizí proto: využívá všechny složky klíče DSA
Hash function h(k) Heavily depends on the key properties and on the representation of the keys Ideally: computationally as simple as possible (fast) approximates a random function uses the address space regularly generates minimum of collisions therefore: uses all key parts DSA
Rozptylovací funkce h(k)-příklady Hash functions h(k) - examples Pro reálná čísla 0..1 - for real numbers multiplikativní: h(k,M) = round( k * M ) (neoddělí shluky blízkých čísel) (does not separate clusters of similar numbers) M = velikost tabulky DSA
Rozptylovací funkce h(k)-příklady Pro celá čísla (w-bitová) - for w-bit integers multiplikativní: (kde M je prvočíslo-prime) h(k,M) = round( k /2w * M ) modulární: h(k,M) = k % M kombinovaná (combined): h(k,M) = round( c * k ) % M, c <0,1> h(k,M) = (int)(.616161 * (float) k ) % M h(k,M) = (16161 * (unsigned) k) % M DSA
Rozptylovací funkce h(k)-příklady Hash functions h(k) - examples Rychlá, silně závislá na reprezentaci klíčů (Fast, heavily dependent on the representation of keys in memory): h(k) = k & (M-1) pro M = 2x (není prvočíslo) , a & = bitový součin (M not prime, & = bit-wise AND) DSA
Rozptylovací funkce h(k)-příklady Pro řetězce (for strings): int hash( char *k, int M ) { int h = 0, a = 127; for( ; *k != 0; k++ ) h = ( a * h + *k ) % M; return h; } // Hornerovo schéma: k2 * a2 + k1*a1 + k0 * a0 = ((k2 * a) + k1)*a + k0 DSA
Rozptylovací funkce h(k)-příklady Pro řetězce: (pseudo-) randomizovaná int hash( char *k, int M ) { int h = 0, a = 31415; b = 27183; for( ; *k != 0; k++, a = a*b % (M-1) ) h = ( a * h + *k ) % M; return h; } // Univerzální rozptylovací fce kolize s pravděpodobností 1/M odlišná náhodná konstanta pro různé pozice v řetězci DSA
Hash functions h(k) - examples For strings: (pseudo-) randomized int hash( char *k, int M ) { int h = 0, a = 31415; b = 27183; for( ; *k != 0; k++, a = a*b % (M-1) ) h = ( a * h + *k ) % M; return h; } // Universal hashing function chance of collisions between distinct keys exactly 1/M different random constant for different string positions DSA
Rozptylovací funkce h(k)-chyba funkce vrací stejnou hodnotu chyba v konverzi funguje, ale vrací blízké adresy proto generuje hodně kolizí => aplikace je extrémně pomalá DSA
Hash functions h(k) - performance bug always to return the same value conversion did not take place properly generates many collisions program is likely to run correctly, but to be extremely slow DSA
2. Vyřešení kolizí Handling of collisions DSA
a) Zřetězené rozptylování 1/5 Separate chainimg h(k) = k mod 3 posloupnost (sequence) : 1, 5, 21, 10, 7 heads link 1 2 21 \ 7 10 1 \ 5 \ seznamy synonym DSA
a) Zřetězené rozptylování 2/5 private: link* heads; int N,M; [Sedgewick] public: ST( int maxN ) // initialization { N=0; M = maxN / 5; // No.nodes,table size heads = new link[M];// table with pointers for( int i = 0; i < M; i++ ) heads[i] = null; }... DSA
a) Zřetězené rozptylování 3/5 Item search( Key k ) { return searchList( heads[ hash(k, M)], k );} void insert( Item item ) { int i = hash( item.key(), M ); heads[i] = new node( item, heads[i] ); N++; } DSA
a) Zřetězené rozptylování 4/5 Řetěz synonym má ideálně délku =n/m, >1 (n = počet prvků, m = velikost tabulky, m<n) Insert I(n) = thash + tlink = O(1) Search Q(n) = thash + tsearch průměrně = thash + tc* n/(2m) = O(n) O(1 + ) Delete D(n) = thash + tsearch + tlink = O(n) O(1 + ) pro malá (velká m) se hodně blíží O(1) !!! pro velká (malá m) m-násobné zrychlení x seq. s. velmi nepravděpodobný extrém DSA
a) Zřetězené rozptylování 5/5 Praxe: volit m = n/5 až n/10 => = 10 prvků / řetěz vyplatí se hledání sekvenčně neplýtvá nepoužitými ukazateli Shrnutí: + nemusíme znát n předem - potřebuje dynamické přidělování paměti - potřebuje paměť na ukazatele a na tabulku[m] DSA
b) Otevřené rozptylování (open-addressing hashing) Znám předem počet prvků (odhad) nechci ukazatele (v prvcích ani tabulku) => posloupnost do pole Podle tvaru hashovací funkce h(k) při kolizi 1. linear probing 2. double hashing DSA
b) Otevřené rozptylování h(k) = k mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 3 4 kolize - 1 blokuje 1. linear probing 2. double hashing Pozn.: 1 a 21 jsou synonyma často ale blokuje nesynonymum DSA
Probe = určení, zda pozice v tabulce obsahuje klíč shodný s hledaným klíčem search hit = klíč nalezen search miss = pozice prázdná, klíč nenalezen jinak = na pozici je jiný klíč, hledej dál DSA
Probe = determining, whether or not a given table position holds an item with a key equal to the search key search hit = key matches search miss = the table position is empty otherwise = the table position contains an item with a different key, so probe the the table position with a next higher index DSA
b) Otevřené rozptylování (open-addressing hashing) b1) Linear probing
b1) Linear probing h(k) = [(k mod 5) + i ] mod 5 = (k + i) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 21 3 4 kolize - 1 blokuje vlož o pozici dál (i++ => i = 1) insert one position ahead DSA
b1) Linear probing h(k) = (k + i) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 21 3 10 4 1. kolize - 5 blokuje - vlož dál 2. kolize - 1 blokuje - vlož dál 3. kolize - 21 blokuje - vlož dál vloženo o 3 pozice dál (i = 3) collision - position blocked - i++ finally inserted 3 indexes ahead DSA
b1) Linear probing h(k) = (k + i) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 21 3 10 4 7 1. kolize - vlož dál (i++) 2. kolize - vlož dál (i++) vlož o 2 pozice dál (i = 2) DSA
b1) Linear probing h(k) = (k + i) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 21 3 10 4 7 i = 0 i = 1 i = 3 i = 2 DSA
b1) Linear probing private: Item *st; int N,M; [Sedgewick] Item nullItem; public: ST( int maxN ) // initialization { N=0; M = 2*maxN; // load_factor < 1/2 st = new Item[M]; for( int i = 0; i < M; i++ ) st[i] = nullItem; }... DSA
b1) Linear probing void insert( Item item ) { int i = hash( item.key(), M ); while( !st[i].null() ) i = (i+1) % M; // Linear probing st[i] = item; N++; } DSA
b1) Linear probing Item search( Key k ) { int i = hash( k, M ); while( !st[i].null() ) { // zarážka if( k == st[i].key() ) return st[i]; else i = (i+1) % M; // Linear probing } return nullItem; DSA
b) Otevřené rozptylování (open-addressing hashing) b2) Double hashing
b2) Double hashing h(k) = k mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 0 5 1 1 2 3 4 kolize - 1 blokuje (collision-blocks) 1. linear probing 2. double hashing DSA
b2) Double hashing h(k) = [(k mod 5) + i.h2(k) ] mod 5, h(k) = (k + i.3) mod 5 posloupnost: 1, 5, 21, 10, 7 stačí prvočíslo m prime number m 0 5 1 1 2 3 4 21 kolize - 1 blokuje vlož o 3 pozice dál (i++ => i = 1) DSA
b2) Double hashing h(k) = (k + i.3) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 3 10 4 21 1. kolize - 5 blokuje - vlož dál vlož o 3 pozice dál (i = 1) DSA
b2) Double hashing h(k) = (k + i.3) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 7 3 10 4 21 i = 0 DSA
b2) Double hashing h(k) = (k + i.3) mod 5 posloupnost: 1, 5, 21, 10, 7 0 5 1 1 2 7 3 10 4 21 DSA
Double hashing x Linear probing h(k) = (k + i.3) mod 5 h(k) = (k + i) mod 5 i = 0 i = 1 0 5 1 1 2 21 3 10 4 7 i = 0 i = 1 i = 3 ! i = 2 0 5 1 1 2 7 3 10 4 21 vnořené sekvence (mixed probe sequences) dlouhé shluky (long clusters) DSA
b2) Double hashing void insert( Item item ) { Key k = item.key(); int i = hash( v, M ), j = hashTwo( v, M ); while( !st[i].null() ) i = (i+j) % M; // Double Hashing st[i] = item; N++; } DSA
b2) Double hashing Item search( Key k ) { int i = hash( k, M ), j = hashTwo( v, M ); while( !st[i].null() ) { if( k == st[i].key() ) return st[i]; else i = (i+j) % M; // Double Hashing } return nullItem; DSA
Linear-probing Item Removal // do not leave the hole - it can break a cluster void remove( Item item ) { int i = hash( v, M ), j; while( !st[i].null() ) // find item to remove if( item.key() == st[i].key() ) break; else i = (i+1) % M; if( st[i].null() ) return; // not found st[i] = nullItem; N--; // delete, reinsert for( j = i+1; !st[j].null(); (j+1) % M, N--) { Item v = st[j]; st[j] = nullItem; insert(v); } } DSA
Double-hashing Item Removal // Double Hashing - overlapping search sequences // - fill up the hole by sentinel // - skipped by search, replaced by insert void remove( Item item ) { int i = hash( v, M ), j = hashTwo( v, M ); while( !st[i].null() ) // find item to remove if( item.key() == st[i].key() ) break; else i = (i+j) % M; if( st[i].null() ) return; // not found st[i] = sentinelItem; N--; // delete } DSA
b) Otevřené rozptylování (open-addressing hashing) = zaplnění tabulky (load factor of the table) = n/m, 0,1 n = počet prvků (number of items in the table) m = velikost tabulky, m>n (table size) DSA
b) Otevřené rozptylování (open-addressing hashing) Average number of probes [Sedgewick] Linear probing: Search hits 0.5 ( 1 + 1 / (1 - ) ) Search misses 0.5 ( 1 + 1 / (1 - )2 ) Double hashing: Search hits (1 / ) ln ( 1 / (1 - ) ) Search misses 1 / (1 - ) DSA
b) Očekávaný počet testů Linear probing: Plnění 1/2 2/3 3/4 9/10 Search hit 1.5 2.0 3.0 5.5 Search miss 2.5 5.0 8.5 55.5 Double hashing: Search hit 1.4 1.6 1.8 2.6 Search miss 1.5 2.0 3.0 5.5 Tabulka může být více zaplněná než začne klesat výkonnost. K dosažení stejného výkonu stačí menší tabulka. DSA
b) Expected number of probes Linear probing: Load factor 1/2 2/3 3/4 9/10 Search hit 1.5 2.0 3.0 5.5 Search miss 2.5 5.0 8.5 55.5 Double hashing: Search hit 1.4 1.6 1.8 2.6 Search miss 1.5 2.0 3.0 5.5 - Table can be more full before performance degrades. - We need smaller table for the same performance. DSA