8..007 Datové struktury a algoritmy Vyhlednávání Topics ást Vyhledávání, zejména rozptylování Petr Felkel Rozptylování (hashing) Rozptylovací funkce ešení kolizí Z et zené rozptylování Otev ené rozptylování Linear Probing Double hashing DSA 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 Vyhledávání Porovnáváním klí (log n) Nalezeno, když klí _prvku = hledaný klí nap. sekven ní vyhledávání, BVS,... Indexováním klí em (p ímý p ístup) () klí je p ímo indexem (adresou) rozsah klí ~ rozsahu index Rozptylováním pr m rn () výpo tem adresy z hodnoty klí e DSA asociativní adresní vyhledávání 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) málo asu i pam ti - hashing - velikost tabulky reguluje as vyhledání 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 podmnožiny a azení (select a sort) DSA 5 DSA 6
použité klí e 0 Rozptylování Rozptylování vhodné pro K << U K K množina použitých klí U universum klí možné klí e = Universum klí DSA 7 U U 0 K Rozptylování h(0) h() h()? h(k) h() 0 0... Dv fáze M-. Výpo et rozptylovací funkce h(k) (h(k) vypo ítá adresu z hodnoty klí e). Vy ešení kolizí h()... kolize: index již obsazen DSA 8 Rozptylovací funkce h(k). Výpo et rozptylovací funkce h(k) Zobrazuje množinu klí K U do intervalu adres A = <a min, a max >, obvykle <0,M-> U >> K ~= A (h(k) Vypo ítá adresu z hodnoty klí e) Synonyma: k k, h(k ) = h(k ) = kolize DSA 9 DSA 0 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 Rozptylovací funkce h(k) Každá hashovací funkce má slabá místa, kdy pro r zné klí e dává stejnou adresu Univerzální hashování Místo jedné hashovací funkce h(k) máme kone nou množinu H funkcí mapujících U do intrervalu 0,,, m- P i spušt ní programu jednu náhodn zvolíme Tato množina je univerzální, pokud pro r zné klí e x,y U vrací stejnou adresu h(x) = h(y) p esn v H /m p ípadech Pravd podobnost kolize p i náhodném výb ru funkce h(k) je tedy p esn /m DSA DSA
Rozptylovací funkce h(k) -p íklady P íklady fce h(k) pro r zné typy klí reálná ísla celá ísla bitová et zce Chybná rozptylovací funkce Rozptylovací funkce h(k)-p íklady Pro reálná ísla 0.. multiplikativní: h(k,m) = round( k * M ) (neodd lí shluky blízkých ísel) M = velikost tabulky (table size) DSA DSA Rozptylovací funkce h(k)-p íklady Pro celá ísla (w-bitová) - for w-bit integers multiplikativní: (kde M je prvo íslo) h(k,m) = round( k / w * M ) modulární: h(k,m) = k % M kombinovaná: h(k,m) = round( c * k ) % M, c <0,> h(k,m) = (int)(0.666 * (float) k ) % M h(k,m) = (66 * (unsigned) k) % M Rozptylovací funkce h(k)-p íklady Hash functions h(k) - examples Rychlá, siln závislá na reprezentaci klí h(k) = k & (M-) pro M = x (není prvo íslo), a & = bitový sou in DSA 5 DSA 6 Rozptylovací funkce h(k)-p íklady Rozptylovací funkce h(k)-p íklady Pro et zce (for strings): Pro et zce: (pseudo-) randomizovaná int hash( char *k, int M ) int h = 0, a = 7; for( ; *k!= 0; k++ ) h = ( a * h + *k ) % M; return h; Hornerovo schéma: k * a + k *a + k 0 * a 0 = ((k * a) + k )*a + k 0 int hash( char *k, int M ) int h = 0, a = 5; b = 78; for( ; *k!= 0; k++, a = a*b % (M-) ) h = ( a * h + *k ) % M; return h; Univerzální rozptylovací fce kolize s pravd podobností /M odlišná náhodná konstanta pro r zné pozice v et zci DSA 7 DSA 8
Rozptylovací funkce h(k)-chyba astá chyba: funkce vrací stále stejnou hodnotu chyba v konverzi typ funguje, ale vrací blízké adresy proto generuje hodn kolizí => aplikace je extrémn pomalá Rozptylovací funkce h(k) Shrnutí -po ítá adresu z hodnoty klí e DSA 9 DSA 0 a) Z et zené rozptylování /5 Chaining. Vy ešení kolizí h(k) = k mod posloupnost :, 5,, 0, 7 heads link 0 \ 7 5 \ 0 Vkládá se na za átek \ seznamy synonym DSA DSA a) Z et zené rozptylování /5 private: link* heads; int N,M; [Sedgewick] public: init( int maxn ) // initialization N=0; // No.nodes M = maxn / 5; // table size heads = new link[m]; // table with pointers for( int ; i < M; i++ ) heads[i] = null;... a) Z et zené rozptylování /5 Item search( Key k ) return searchlist( heads[hash(k, M)], k ); void insert( Item item ) // Vkládá se na za átek int i = hash( item.key(), M ); heads[i] = new node( item, heads[i] ); N++; DSA DSA
a) Z et zené rozptylování /5 et z synonym má ideáln délku =n/m, > (pln ní tabulky) (n = po et prvk, m = velikost tabulky, m<n) velmi nepravd podobný Insert I(n) = t hash + t link = O() extrém Search Q(n) = t hash + t search pr m rn = t hash + t c * n/(m) = O(n) O( + ) Delete D(n) = t hash + t search + t link = O(n) O( + ) a) Z et zené rozptylování 5/5 Praxe: volit m = n/5 až n/0 => pln ní = 0 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] pro malá (velká m) se hodn blíží O()!!! pro velká (malá m) m-násobné zrychlení x seq. s. DSA 5 DSA 6 b) Otev ené rozptylování (open-address 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. lineární prohledávání (linear probing). dvojí rozptylování (double hashing) 0 DSA 7 b) Otev ené rozptylování h(k) = k mod 5 (h(k) = k mod m, m je rozm r pole) Problém: kolize - blokuje místo pro. linear probing. double hashing Pozn.: a jsou synonyma asto ale blokuje nesynonymum. Kolize je blokování libovolným klí em DSA 8 Test - 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 b) Otev ené rozptylování (open-addressing hashing) Metoda ešení kolizí (solution of collisions) Lineární prohledávání Dvojí rozptylování DSA 9 DSA 0
h(k) = [(k mod 5) + i ] mod 5 = (k + i) mod 5 kolize - blokuje =>. linear probing vlož o pozici dál (i++ => i = ) 0 h(k) = (k + i) mod 5. kolize - 5 blokuje - vlož dál. kolize - blokuje - vlož dál. kolize - blokuje - vlož dál vloženo o pozice dál (i = ) DSA DSA h(k) = (k + i) mod 5 0 7. kolize - vlož dál (i++). kolize - vlož dál (i++) vlož o pozice dál (i = ) h(k) = (k + i) mod 5 i = 0 i = 7 i = DSA DSA private: Item *st; int N,M; [Sedgewick] Item nullitem; public: init( int maxn ) // initialization N=0; // Number of stored items M = *maxn; // load_factor < / st = new Item[M]; for( int ; i < M; i++ ) st[i] = nullitem;... void insert( Item item ) int i = hash( item.key(), M ); while(!st[i].null() ) i = (i+) % M; // Linear probing st[i] = item; N++; DSA 5 DSA 6
Item search( Key k ) int i = hash( k, M ); while(!st[i].null() ) //!cluster end // zarážka (sentinel) if( k == st[i].key() ) return st[i]; else i = (i+) % M; // Linear probing return nullitem; b) Otev ené rozptylování (open-addressing hashing) Metoda ešení kolizí (solution of collisions) Lineární prohledávání Dvojí rozptylování DSA 7 DSA 8 Hash function h(k) = [h (k) + i.h (k) ] mod m h (k) = k mod m // initial position h (k) = + (k mod m ) // offset Both depend on k => Each key has different probe sequence m = prime number or m = power of m = slightly less m = odd If d = greatest common divisor => search /d slots only Ex: k = 56, m = 70, m = 700 h (k) = 80, h (k) = 57 Starts at 80, and every 57 % 70 DSA 9 h(k) = k mod 5 kolize - blokuje (collision-blocks) =>. double hashing DSA 0 Nebo zjednodušen (or simplified) h(k) = [(k mod 5) + i.h (k) ] mod 5, h(k) = (k + i.) mod 5 sta í prvo íslo m kolize - blokuje (collision blocks) =>. double hashing vlož o pozice dál (i++ => i = ) (i je íslo pokusu) h(k) = (k + i.) mod 5 (P. velmi zjednodušené h(k)) 0 kolize - 5 blokuje - vlož dál (vlož o pozice dál (i = ) DSA DSA
h(k) = (k + i.) mod 5 (P. velmi zjednodušené h(k)) 7 0 h(k) = (k + i.) mod 5 (P. velmi zjednodušené h(k)) 7 0 i = i = DSA DSA Linear probing x Double hashing h(k) = (k + i) mod 5 h(k) = (k + i.) mod 5 0 7 i = i =! i = 7 0 i = i = void insert( Item item ) Key k = item.key(); int i = hash( k, M ), j = hashtwo( k, M );// different for k!= k while(!st[i].null() ) i = (i+j) % M; //Double Hashing st[i] = item; N++; dlouhé shluky (long clusters) vno ené sekvence (mixed probe sequences) DSA 5 DSA 6 Item search( Key k ) int i = hash( k, M ), j = hashtwo( k, M );// different for k!= k while(!st[i].null() ) if( k == st[i].key() ) return st[i]; else i = (i+j) % M; // Double Hashing return nullitem; Double hashing - example h(k) = [h (k) + i.h (k) ] mod m Input 5 5 0 0 h (k)= k % 9 h (k)= +k %0 6 6 i 0 0 0, 0, 0, 0 h(k),5,7,6 9 5 6 7 8 0 9 0 5 0 5 0 h (k) = k % h (k) = + (k % 0) DSA 7 DSA 8
Item removal (delete) Item x removal x replaced by null null breaks cluster(s)!!! => do not leave the hole after delete k k k x \ before \ \ break cluster Unreachable cluster parts Correction different for linear probing and double hashing b) in linear probing \ \ => reinsert the items after x (to the first null = to cluster end) b) in double hashing = \ => fill the hole up by a special sentinel skipped by search, replaced by insert Item removal (delete) k k k b) in linear probing \ \ Unreachable cluster parts => reinsert the items behined the cluster break (to the null) \ \ Remained => avoid simple move of cluster tail Reinserted to different place it can make other keys not accessible!!! k k k X DSA 9 DSA 50 Linear-probing Item Removal // do not leave the hole - can break a cluster void remove( Item item ) Key k = item.key(); int i = hash( k, M ), j; while(!st[i].null() )// find item to remove if( item.key() == st[i].key() ) break; else i = (i+) % M; if( st[i].null() ) return; // not found st[i] = nullitem; N--; //delete,reinsert for(j = i+;!st[j].null(); j=(j+)%m, N--) Item v = st[j]; st[j] = nullitem; insert(v); //reinsert elements after deleted DSA 5 Item removal (delete) h(k) = [h (k) + i.h (k) ] mod m 5 6 7 8 0 9 0 5 0 5 0 null breaks paths to and 0? 5 6 7 8 0 9 0 0 5 0? h (k) = k % h (k) = + (k % 0) Remove 5 Sentinel is correct 5 6 7 8 0 9 0 = 0 5 0 DSA 5 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 ) Key k = item.key(); int i = hash( k, M ), j = hashtwo( k, 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 = replace b) Otev ené rozptylování (open-addressing hashing) = pln ní tabulky (load factor of the table) = n/m, 0, n = po et prvk (number of items in the table) m = velikost tabulky, m>n (table size) DSA 5 DSA 5
b) Otev ené rozptylování (open-addressing hashing) Average number of probes [Sedgewick] Linear probing: Search hits 0.5 ( + / ( - ) ) found Search misses 0.5 ( + / ( - ) ) not found Double hashing: Search hits ( / ) ln ( / ( - ) ) + ( / ) Search misses / ( - ) = n/m, 0, b) O ekávaný po et test Linear probing: Pln ní / / / 9/0 Search hit.5.0..5 Search miss.5 5.0 8.5 55.5 0 více test Double hashing: Pln ní / / / 9/0 Search hit..6.8.6 Search miss.5.0..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 55 DSA 56 References [Cormen] Cormen, Leiserson, Rivest: Introduction to Algorithms, Chapter, McGraw Hill, 990 DSA 57