Hašovacia tabuľka: Rozdiel medzi revíziami

z Wikipédie, slobodnej encyklopédie
Smazaný obsah Přidaný obsah
Bronto (diskusia | príspevky)
opravil som sh začiatku textu
Maros (diskusia | príspevky)
Bez shrnutí editace
Riadok 1: Riadok 1:
{{preklad}} [[:en:hash table]]
{{preklad}} [[:en:hash table]]
{{pracuje sa (2)}}
{{pracuje sa (2)}}
'''Hašovacia tabuľka''' alebo '''hašovacia mapa''' alebo '''tabuľka výpočtu adresy transformáciou (kľúča)''' je v [[informatika|informatike]] [[údajová štruktúra]], ktorá asociuje ''kľúče'' s ''hodnotami''. Primárna efektívne podporovaná operácia je ''vyhľadávanie'': pri zadaní kľúča (napr. meno osoby) nájsť zodpovedajúcu hodnotu (napr. telefónne číslo tejto osoby). Pracuje vďaka transformácii kľúča [[hašovacia funkcia|hašovacou funkciou]] na ''hash'', číslo, ktoré tabuľka používa na nájdenie požadovanej hodnoty.
'''Hašovacia tabuľka''' alebo '''hašovacia mapa''' alebo '''tabuľka výpočtu adresy transformáciou (kľúča)''' je v [[informatika|informatike]] [[údajová štruktúra]], ktorá asociuje ''kľúče'' s ''hodnotami''. Primárna efektívne podporovaná operácia je ''vyhľadávanie'': pri zadaní kľúča (napr. meno osoby) nájsť zodpovedajúcu hodnotu (napr. telefónne číslo tejto osoby). Pracuje vďaka transformácii kľúča [[hašovacia funkcia|hašovacou funkciou]] na ''haš'', číslo, ktoré tabuľka používa na nájdenie požadovanej hodnoty.


[[Image:HASHTB08_sk.svg|thumb|362px|right|Malý telefónny zoznam ako hashovacia tabuľka.]]
[[Image:HASHTB08_sk.svg|thumb|362px|right|Malý telefónny zoznam ako hašovacia tabuľka.]]


Hašovacie tabuľky sa často používajú na implementáciu [[asociatívne pole|asociatívnych polí]], [[množina (informatika)|množín]] a [[rýchla vyrovnávacia pamäť|rýchlych vyrovnávacích]] pamätí (cache). Rovnako ako [[pole (údajová štruktúra)|polia]], hašovacie tabuľky poskytujú vyhľadávanie s konštantným časom [[Notácia veľké O|O]](1) v priemernom prípade, nezávisle na počte položiek v tabuľke. Avšak zriedkavý najhorší prípad môže byť až O(''n''). V porovnaní s inými údajovými štruktúrami asociatívnych polí sú hashovacie tabuľky najužitočnejšie pre uloženie veľkého množstva údajov.
Hašovacie tabuľky sa často používajú na implementáciu [[asociatívne pole|asociatívnych polí]], [[množina (informatika)|množín]] a [[rýchla vyrovnávacia pamäť|rýchlych vyrovnávacích]] pamätí (cache). Rovnako ako [[pole (údajová štruktúra)|polia]], hašovacie tabuľky poskytujú vyhľadávanie s konštantným časom [[Notácia veľké O|O]](1) v priemernom prípade, nezávisle na počte položiek v tabuľke. Avšak zriedkavý najhorší prípad môže byť až O(''n''). V porovnaní s inými údajovými štruktúrami asociatívnych polí sú hašovacie tabuľky najužitočnejšie pre uloženie veľkého množstva údajov.


Hašovacie tabuľky ukladajú údaje na pseudonáhodných miestach, preto prístup k údajom utriedeným spôsobom je veľmi časovo náročná operácia. Iné údajové štruktúry ako [[samovyvažovací binárny vyhľadávací strom|samovyvažovací binárny vyhľadávacie stromy]] sú vo všeobecnosti oveľa pomalšie (keďže zložitosť vyhľadávania majú O(log ''n'')) a sú skôr zložitejšie na implementáciu ako hašovacie tabuľky, ale udržiavajú si neustále utriedenú štruktúru položiek. Pozri [[porovnanie hašovacích tabuliek a samovyvažovacích binárnych vyhľadávacích stromov]].
Hašovacie tabuľky ukladajú údaje na pseudonáhodných miestach, preto prístup k údajom utriedeným spôsobom je veľmi časovo náročná operácia. Iné údajové štruktúry ako [[samovyvažovací binárny vyhľadávací strom|samovyvažovací binárny vyhľadávacie stromy]] sú vo všeobecnosti oveľa pomalšie (keďže zložitosť vyhľadávania majú O(log ''n'')) a sú skôr zložitejšie na implementáciu ako hašovacie tabuľky, ale udržiavajú si neustále utriedenú štruktúru položiek. Pozri [[porovnanie hašovacích tabuliek a samovyvažovacích binárnych vyhľadávacích stromov]].


== Prehľad ==
== Prehľad ==
Základné operácie, ktoré hashovacie tabuľky vo všeobecnosti podporujú sú:
Základné operácie, ktoré hašovacie tabuľky vo všeobecnosti podporujú sú:
:<code>vlož(kľúč, hodnota)</code>
:<code>vlož(kľúč, hodnota)</code>
:<code>nájdi(kľúč)</code> ktorá vráti <code>hodnotu</code>
:<code>nájdi(kľúč)</code> ktorá vráti <code>hodnotu</code>
Väčšina, ale nie všetky hashovacie tabuľky podporujú <code>zmaž(kľúč)</code>. Môžu existovať aj iné služby ako iterovanie tabuľkou, zväčšenie tabuky, vyprázdnenie tabuľky. Niektoré tabuľky umožňujú uloženie viacerých hodnôt pod rovnakým kľúčom.
Väčšina, ale nie všetky hašovacie tabuľky podporujú <code>zmaž(kľúč)</code>. Môžu existovať aj iné služby ako iterovanie tabuľkou, zväčšenie tabuky, vyprázdnenie tabuľky. Niektoré tabuľky umožňujú uloženie viacerých hodnôt pod rovnakým kľúčom.


Kľúčom môže byť v podstate čokoľvek -- číslo, reťazec, objekt; niektoré tabuľky ukladajú dokonca aj odkaz na hodnotu. Jedinou požiadavkou je existencia [[hašovacia funkcia|hašovacej funkcie]] pre použité kľúče (pozri nižšie).
Kľúčom môže byť v podstate čokoľvek -- číslo, reťazec, objekt; niektoré tabuľky ukladajú dokonca aj odkaz na hodnotu. Jedinou požiadavkou je existencia [[hašovacia funkcia|hašovacej funkcie]] pre použité kľúče (pozri nižšie).
Riadok 21: Riadok 21:
Pretože počet platných kľúčov je zvyčajne oveľa väčší ako rozsah platných indexov do poľa, je potrebný spôsob konverzie každého kľúča na platný index. Toto vykonáva [[hašovacia funkcia]], čo je funkcia, ktorá berie ako argument kľúč a vracia index do poľa. Indexovaný element poľa by zasa mal obsahovať záznam asociovaný s daným kľúčom.
Pretože počet platných kľúčov je zvyčajne oveľa väčší ako rozsah platných indexov do poľa, je potrebný spôsob konverzie každého kľúča na platný index. Toto vykonáva [[hašovacia funkcia]], čo je funkcia, ktorá berie ako argument kľúč a vracia index do poľa. Indexovaný element poľa by zasa mal obsahovať záznam asociovaný s daným kľúčom.


Avšak preto, že existuje viac potenciálnych kľúčov ako indexov do poľa, možno dokázať (?Dirichletovym princípom), že dva alebo viac potenciálnych kľúčov budú mať rovnaký hash; toto sa nazýva ''kolízia''. Hašovacia funkcia by mala byť navrhnutá tak, aby minimalizovala množstvo kolízií pre akýkoľvek vrátený index.
Avšak preto, že existuje viac potenciálnych kľúčov ako indexov do poľa, možno dokázať (?Dirichletovym princípom), že dva alebo viac potenciálnych kľúčov budú mať rovnaký haš; toto sa nazýva ''kolízia''. Hašovacia funkcia by mala byť navrhnutá tak, aby minimalizovala množstvo kolízií pre akýkoľvek vrátený index.


Keďže v jednom elemente poľa je možné uložiť iba jednu položku, musí byť použitá stratégia [[Hašovacia tabuľka#Riešenie kolízií|riešenia kolízií]]. Napríklad sa kolízny záznam uloží do nasledujúceho voľného elementu alebo môžu elementy poľa ukladať [[spojový zoznam]] záznamov.
Keďže v jednom elemente poľa je možné uložiť iba jednu položku, musí byť použitá stratégia [[Hašovacia tabuľka#Riešenie kolízií|riešenia kolízií]]. Napríklad sa kolízny záznam uloží do nasledujúceho voľného elementu alebo môžu elementy poľa ukladať [[spojový zoznam]] záznamov.


Hoci sa nejaké kolízie vyskytnú, s dobrými hašovacími funkciami a tabuľkou plnou asi na 80 %, budú kolízie relatívne zriedkavé a výkon veľmi dobrý, keďže priemerne sa vykoná veľmi málo porovnaní. Avšak ak sa tabuľka príliš zaplní, výkon bude nízky a bude potrebné zväčšiť hashovaciu tabuľku. Zväčšenie tabuľky ([[Hashovacia tabuľka#Zmena veľkosti tabuľky|Zmena veľkosti tabuľky]]) znamená, že sa budú musieť znova pridať všetky záznamy.
Hoci sa nejaké kolízie vyskytnú, s dobrými hašovacími funkciami a tabuľkou plnou asi na 80 %, budú kolízie relatívne zriedkavé a výkon veľmi dobrý, keďže priemerne sa vykoná veľmi málo porovnaní. Avšak ak sa tabuľka príliš zaplní, výkon bude nízky a bude potrebné zväčšiť hašovaciu tabuľku. Zväčšenie tabuľky ([[Hašovacia tabuľka#Zmena veľkosti tabuľky|Zmena veľkosti tabuľky]]) znamená, že sa budú musieť znova pridať všetky záznamy.


Väčšina implementácií hashovacích tabuliek nie sú [[perzistentná údajová štruktúra|perzistentnými údajovými štruktúrami]] v zmysle, že neexistuje spôsob ako ich aktualizovať a zároveň si udržať prístup k predchádzajúcej kópii hashovacej tabuľky (hoci sú perzistentné v zmysle uloženia na disku). Niektoré implementácie hashovacích tabuliek ako tie, ktoré používajú [[VList]], sú perzistentné, ale v praxi sa zriedkavo používajú a nie sú také výkonné ako narmálne hashovacie tabuľky kvôli zvýšenému času na indexáciu.
Väčšina implementácií hašovacích tabuliek nie sú [[perzistentná údajová štruktúra|perzistentnými údajovými štruktúrami]] v zmysle, že neexistuje spôsob ako ich aktualizovať a zároveň si udržať prístup k predchádzajúcej kópii hašovacej tabuľky (hoci sú perzistentné v zmysle uloženia na disku). Niektoré implementácie hašovacích tabuliek ako tie, ktoré používajú [[VList]], sú perzistentné, ale v praxi sa zriedkavo používajú a nie sú také výkonné ako narmálne hašovacie tabuľky kvôli zvýšenému času na indexáciu.


== Bežné využitie hashovacích tabuliek ==
== Bežné využitie hašovacích tabuliek ==
Hashovacie tabuľky sa bežne využívajú pre [[tabuľka symbolov|tabuľky symbolov]], [[Rýchla vyrovnávacia pamäť|rýchle vyrovnávacie pamäte]] a [[množina (informatika)|množiny]].
Hašovacie tabuľky sa bežne využívajú pre [[tabuľka symbolov|tabuľky symbolov]], [[Rýchla vyrovnávacia pamäť|rýchle vyrovnávacie pamäte]] a [[množina (informatika)|množiny]].


Hashovacie tabuľky nie sú vo všeobecnosti [[perzistentná údajová štruktúra|perzistentnými údajovými štruktúrami]] v tom zmysle, že neexistuje jednoduchý a vzhľadom na úložný priestor efektívny spôsob, ako aktualizovať hashovaciu tabuľku a zároveň si si udržať prístup k jej predchádzajúcej kópii. Je to však možné implementovaním [[VList]]u, čím sa stane perzistentnou, ale nepriaznivo to ovplyvní výkon. hashovacie tabuľky môžu byť „perzistentné“ v inom zmysle - môžu byť uložené na disku. Databázové indexy bežne používajú diskové údajové štruktúry založené na hashovacích tabuľkách.
Hašovacie tabuľky nie sú vo všeobecnosti [[perzistentná údajová štruktúra|perzistentnými údajovými štruktúrami]] v tom zmysle, že neexistuje jednoduchý a vzhľadom na úložný priestor efektívny spôsob, ako aktualizovať hašovaciu tabuľku a zároveň si si udržať prístup k jej predchádzajúcej kópii. Je to však možné implementovaním [[VList]]u, čím sa stane perzistentnou, ale nepriaznivo to ovplyvní výkon. hašovacie tabuľky môžu byť „perzistentné“ v inom zmysle - môžu byť uložené na disku. Databázové indexy bežne používajú diskové údajové štruktúry založené na hašovacích tabuľkách.


V [[počítačový šach|počítačovom šachu]] sa hashovacia tabuľka bežne používa na implementáciu [[transpozičná tabuľka|transpozičnej tabuľky]].
V [[počítačový šach|počítačovom šachu]] sa hašovacia tabuľka bežne používa na implementáciu [[transpozičná tabuľka|transpozičnej tabuľky]].


== Voľba dobrej hashovacej funkcie ==
== Voľba dobrej hašovacej funkcie ==
Dobrá hashovacia funkcia má zásadný vplyv na výkon hashovacej tabuľky. Kolízie hashus sa vo všeobecnosti riešia určitou formou [[lineárne vyhľadávanie|lineárneho vyhľadávania]], takže ak hashovacia funkcia zvykne vracať podobné hodnoty, výsledkom bude pomalé vyhľadávanie.
Dobrá hašovacia funkcia má zásadný vplyv na výkon hašovacej tabuľky. Kolízie hašus sa vo všeobecnosti riešia určitou formou [[lineárne vyhľadávanie|lineárneho vyhľadávania]], takže ak hašovacia funkcia zvykne vracať podobné hodnoty, výsledkom bude pomalé vyhľadávanie.


Ideálna hashovacia funkcia by pri každej zmene jednotlivého bitu kľúča (vrátane rozšírenia a skrátenia kľúča) zmenila polovicu bitov hashu a táto zmena by bola nezávislá na zmenách spôsobených inými bitmi kľúča. Pretože môže byť ťažké navrhnúť dobrú hashovaciu funkciu, alebo ju výpočtovo náročné vykonávať, boli do veľkej miery skúmané stratégie riešenia kolízií, ktoré zmierňujú dopad slabého hashovacieho výkonu. Avšak žiadna z nich nie je taká efektívna ako využitie dobrej hashovacej funkcie.
Ideálna hašovacia funkcia by pri každej zmene jednotlivého bitu kľúča (vrátane rozšírenia a skrátenia kľúča) zmenila polovicu bitov hašu a táto zmena by bola nezávislá na zmenách spôsobených inými bitmi kľúča. Pretože môže byť ťažké navrhnúť dobrú hašovaciu funkciu, alebo ju výpočtovo náročné vykonávať, boli do veľkej miery skúmané stratégie riešenia kolízií, ktoré zmierňujú dopad slabého hašovacieho výkonu. Avšak žiadna z nich nie je taká efektívna ako využitie dobrej hašovacej funkcie.


Je žiadúce používať rovnakú hashovaciu funkciu pre polia akejkoľvek mysliteľnej veľkosti. Aby sme to dosiahli, index do poľa hashovacej tabuľky sa vo všeobecnosti počíta v dvoch krokoch:
Je žiadúce používať rovnakú hašovaciu funkciu pre polia akejkoľvek mysliteľnej veľkosti. Aby sme to dosiahli, index do poľa hašovacej tabuľky sa vo všeobecnosti počíta v dvoch krokoch:
#Vypočíta sa všeobecná hodnota hashu, ktorá zaplní veľkosť strojového celého čísla
#Vypočíta sa všeobecná hodnota hašu, ktorá zaplní veľkosť strojového celého čísla
#Táto hodnota sa redukuje na platný index poľa nájdením jeho [[modulo|modula]] s ohľadom na veľkosť poľa.
#Táto hodnota sa redukuje na platný index poľa nájdením jeho [[modulo|modula]] s ohľadom na veľkosť poľa.
Veľkosti poľa hashovacej tabuľky sa niekedy volia tak, aby boli [[prvočíslo|prvočíslami]]. Robí sa to, aby sme sa vyhli výskytu spoločných deliteľov hashu a veľkosti tabuľky, ktoré by boli príčinou kolízií po operácii modulo. Ale ani prvočíselná veľkosť tabuľky nie je náhradou za dobrú hashovaciu funkciu.
Veľkosti poľa hašovacej tabuľky sa niekedy volia tak, aby boli [[prvočíslo|prvočíslami]]. Robí sa to, aby sme sa vyhli výskytu spoločných deliteľov hašu a veľkosti tabuľky, ktoré by boli príčinou kolízií po operácii modulo. Ale ani prvočíselná veľkosť tabuľky nie je náhradou za dobrú hašovaciu funkciu.


Bežnou alternatívou k prvočíselným veľkostiam je použitie veľkosti, ktorá je [[mocnina čísla dva|mocninou čísla dva]] a dosahovať operáciu modulo maskovaním bitov. Maskovanie bitov býva výpočtovo významne rýchlejšie ako operácia delenia. V každom prípade je dobrý nápad zariadiť, aby všeobecná hodnota hashu bola zostavená pomocou prvočísel, ktoré nezdieľajú spoločných deliteľov (napr. ako [[:en:coprime]]) s dĺžkou tabuľky.
Bežnou alternatívou k prvočíselným veľkostiam je použitie veľkosti, ktorá je [[mocnina čísla dva|mocninou čísla dva]] a dosahovať operáciu modulo maskovaním bitov. Maskovanie bitov býva výpočtovo významne rýchlejšie ako operácia delenia. V každom prípade je dobrý nápad zariadiť, aby všeobecná hodnota hašu bola zostavená pomocou prvočísel, ktoré nezdieľajú spoločných deliteľov (napr. ako [[:en:coprime]]) s dĺžkou tabuľky.


Prekvapivo bežným problémom, ktoý sa môže vyskytnúť pri hashovaní je ''klastrovanie''. Klastrovanie sa vyskytuje, keď štruktúra hashovacej funkcie spôsobuje, že bežne používané kľúče padajú blízko vedľa seba alebo dokonca po sebe do hashovacej tabuľky. To môže spôsobiť významné zníženie výkonu ako sa tabuľka plní a používajú sa isté stratégie riešenia kolízií ako lineárne reťazenie.
Prekvapivo bežným problémom, ktoý sa môže vyskytnúť pri hašovaní je ''klastrovanie''. Klastrovanie sa vyskytuje, keď štruktúra hašovacej funkcie spôsobuje, že bežne používané kľúče padajú blízko vedľa seba alebo dokonca po sebe do hašovacej tabuľky. To môže spôsobiť významné zníženie výkonu ako sa tabuľka plní a používajú sa isté stratégie riešenia kolízií ako lineárne reťazenie.


Počas debugovania obsluhy kolízií v hashovacej tabuľke je niekedy užitočné použiť hashovacu funkciu, ktorá vždy vracia konštantnú hodnotu, napr. 1, ktorá spôsobí kolíziu po takmer každom vložení.
Počas debugovania obsluhy kolízií v hašovacej tabuľke je niekedy užitočné použiť hašovacu funkciu, ktorá vždy vracia konštantnú hodnotu, napr. 1, ktorá spôsobí kolíziu po takmer každom vložení.


V prostrediach, kde sa záškodník môže pokúšať spomaliť algoritmus tým, že zadáva nepriaznivý vstup, býva dobrým riešením [[univerzálne hashovanie]], schéma, kde náhodne volíme hashovaciu funkciu na začiatku algoritmu. Pretože záškodník nebude vedieť, akú hashovaciu funkciu používame, nebude vedieť určiť, ktorý vstup je nepriaznivý.
V prostrediach, kde sa záškodník môže pokúšať spomaliť algoritmus tým, že zadáva nepriaznivý vstup, býva dobrým riešením [[univerzálne hašovanie]], schéma, kde náhodne volíme hašovaciu funkciu na začiatku algoritmu. Pretože záškodník nebude vedieť, akú hašovaciu funkciu používame, nebude vedieť určiť, ktorý vstup je nepriaznivý.


== Riešenie kolízií ==
== Riešenie kolízií ==
Ak hashovacia funkcia vráti pre dva rôzne kľúče rovnaký index, zodpovedajúce záznamy nemožno uložiť na to isté miesto. Keďže je už obsadené, musíme nájsť iné miesto pre uloženie záznamu a zabezpečiť, aby sme ho našli, keď ho neskôr vyhľadáme.
Ak hašovacia funkcia vráti pre dva rôzne kľúče rovnaký index, zodpovedajúce záznamy nemožno uložiť na to isté miesto. Keďže je už obsadené, musíme nájsť iné miesto pre uloženie záznamu a zabezpečiť, aby sme ho našli, keď ho neskôr vyhľadáme.


Pre predstavu dobrej stratégie riešenia kolízií si predstavte nasledovný výsledok odvodený použitím [[narodeninový paradox|narodeninového paradoxu]]. Aj za predpokladu, že naša hashovacia funkcia vracia indexy [[rovnomerné rozdelenie (diskrétne)|rovnomerne rozložené]] v poli aj pre pole s 1 milónom záznamov, existuje 95% pravdepodobnosť aspoň jednej kolízie predtým, ako bude obsahovať 2500 záznamov.
Pre predstavu dobrej stratégie riešenia kolízií si predstavte nasledovný výsledok odvodený použitím [[narodeninový paradox|narodeninového paradoxu]]. Aj za predpokladu, že naša hašovacia funkcia vracia indexy [[rovnomerné rozdelenie (diskrétne)|rovnomerne rozložené]] v poli aj pre pole s 1 milónom záznamov, existuje 95% pravdepodobnosť aspoň jednej kolízie predtým, ako bude obsahovať 2500 záznamov.


Existuje niekoľko techník riešenia kolízií, ale najpoužívanejšími sú ''reťazenie'' a ''otvorené adresovanie''.
Existuje niekoľko techník riešenia kolízií, ale najpoužívanejšími sú ''reťazenie'' a ''otvorené adresovanie''.
Riadok 63: Riadok 63:
=== Reťazenie ===
=== Reťazenie ===


[[Image:HASHTB32_sk.svg|thumb|362px|right|Kolízia hashu vyriešená reťazením.]]
[[Image:HASHTB32_sk.svg|thumb|362px|right|Kolízia hašu vyriešená reťazením.]]


V najjednoduchšej technike zreťazenej hashovacej tabuľky každé miesto v poli odkazuje na [[zreťazený zoznam]] vložených záznamov, pre ktoré hashovanie kľúča koliduje na rovnakú hodnotu. Pre vloženie je potrebné nájsť správne miesto a pridať ho na jeden koniec; pre mazanie je potrebn0 preh2ad8vanie zoznamu a odstránenie.
V najjednoduchšej technike zreťazenej hašovacej tabuľky každé miesto v poli odkazuje na [[zreťazený zoznam]] vložených záznamov, pre ktoré hašovanie kľúča koliduje na rovnakú hodnotu. Pre vloženie je potrebné nájsť správne miesto a pridať ho na jeden koniec; pre mazanie je potrebn0 preh2ad8vanie zoznamu a odstránenie.


Zreťazené hashovacie tabuľky majú výhodu v porovnaní s tabuľkami s otvorenou adresáciou, že operácia odstránenia je jednoduchá a zmenu veľkosti tabľky je možné odkladať oveľa dlhšie, pretože výkon sa znižuje oveľa elegantnejšie aj ak je miesto obsadené. Vlastne mnohé zreťazené hashovacie tabuľky nepotrebujú zmenu veľkosti vôbec, pretože degradácia výkonu je lineárna so zaplňovaním tabuľky. Napríklad zreťazená hashovacia tabuľka obsahujúca dvojnásobok odporúčanej dátovej kapacity by bola priemerne iba asi dvakrát pomalšia ako rovnaká tabuľka s odporúčanou kapacitou.
Zreťazené hašovacie tabuľky majú výhodu v porovnaní s tabuľkami s otvorenou adresáciou, že operácia odstránenia je jednoduchá a zmenu veľkosti tabľky je možné odkladať oveľa dlhšie, pretože výkon sa znižuje oveľa elegantnejšie aj ak je miesto obsadené. Vlastne mnohé zreťazené hašovacie tabuľky nepotrebujú zmenu veľkosti vôbec, pretože degradácia výkonu je lineárna so zaplňovaním tabuľky. Napríklad zreťazená hašovacia tabuľka obsahujúca dvojnásobok odporúčanej dátovej kapacity by bola priemerne iba asi dvakrát pomalšia ako rovnaká tabuľka s odporúčanou kapacitou.


Zreťazené hashovacie tabuľky dedia nevýhody spojových zoznamov. Ak sa ukladajú malé záznamy, režijné náklady spojového zoznamu môžu byť významné. Ďalšou nevýhodou je, že prechádzanie spojovým zoznamom má nízky [[lokalita referencie|cache výkon]].
Zreťazené hašovacie tabuľky dedia nevýhody spojových zoznamov. Ak sa ukladajú malé záznamy, režijné náklady spojového zoznamu môžu byť významné. Ďalšou nevýhodou je, že prechádzanie spojovým zoznamom má nízky [[lokalita referencie|cache výkon]].


Namiesto zoznamov je možné pre reťazenie použíť alternatívne údajové štruktúry. Napríklad použitím [[samovyvažovací binárny vyhľadávací strom|samovyvažovacieho stromu]]je možné znížiť teoretický čas najhoršieho prípadu z O(''n'') na O(log ''n''). Ale preto, že každý zoznam by mal byť krátky, tento prístup zvyčajne nevedie k zvýšeniu výkonu, iba ak by bola tabuľka navrhnutá na fungovanie pri plnej kapacite alebo vykazovala nazvyčajne vysoké množsto kolízií. Je tiež možné použiť [[dynamické pole|dynamické polia]] pre zníženie režijných nákladov a zvýšenie výkonnosti cache pri malých záznamoch.
Namiesto zoznamov je možné pre reťazenie použíť alternatívne údajové štruktúry. Napríklad použitím [[samovyvažovací binárny vyhľadávací strom|samovyvažovacieho stromu]]je možné znížiť teoretický čas najhoršieho prípadu z O(''n'') na O(log ''n''). Ale preto, že každý zoznam by mal byť krátky, tento prístup zvyčajne nevedie k zvýšeniu výkonu, iba ak by bola tabuľka navrhnutá na fungovanie pri plnej kapacite alebo vykazovala nazvyčajne vysoké množsto kolízií. Je tiež možné použiť [[dynamické pole|dynamické polia]] pre zníženie režijných nákladov a zvýšenie výkonnosti cache pri malých záznamoch.
Riadok 77: Riadok 77:
== Otvorené adresovanie ==
== Otvorené adresovanie ==


[[Image:HASHTB12_sk.svg|thumb|362px|right|Hash collision resolved by linear probing (interval=1).]]
[[Image:HASHTB12_sk.svg|thumb|362px|right|Haš collision resolved by linear probing (interval=1).]]


Hashovacie tabuľky s otvoreným adresovaním môžu ukladať záznamy priamo v poli. Kolízia hashu sa rieši ''skúšaním'' - prehľadávaním alternatívnych miest v poli (''skúšacia sekvencia''), pokým sa buď nenájde cieľový záznam alebo voľné miesto, čo indikuje, že sa v tabuľke dený kľúč nenachádza. Medzi známe skúšacie sekvencie patria:
Hašovacie tabuľky s otvoreným adresovaním môžu ukladať záznamy priamo v poli. Kolízia hašu sa rieši ''skúšaním'' - prehľadávaním alternatívnych miest v poli (''skúšacia sekvencia''), pokým sa buď nenájde cieľový záznam alebo voľné miesto, čo indikuje, že sa v tabuľke dený kľúč nenachádza. Medzi známe skúšacie sekvencie patria:
:[[lineárne hľadanie]]
:[[lineárne hľadanie]]
::v ktorom je interval medzi skúškami fixný -- často 1,
::v ktorom je interval medzi skúškami fixný -- často 1,
:[[kvadratické skúšanie]]
:[[kvadratické skúšanie]]
::v ktorom interval medzi skúškami rastie lineárne (takže indexy sú opísané kvadratickou funkciou),
::v ktorom interval medzi skúškami rastie lineárne (takže indexy sú opísané kvadratickou funkciou),
:[[dvojité hashovanie]]
:[[dvojité hašovanie]]
::v ktorom je interval medzi skúškami fixný pre každý záznam, ale počíta sa inou hashovacou funkciou
::v ktorom je interval medzi skúškami fixný pre každý záznam, ale počíta sa inou hašovacou funkciou


Hlavné rozdiely medzi týmito metódami sú, že lineárne hľadanie má najlepší cache výkon ale je najcitlivejšie na klastrovanie, kým dvojité hashovanie má nízky cache výkon ale nevykazuje v podstate žiadne klastrovanie; kvadratické hashovanie spadá v oboch oblastiach dostredu. Dvojité hashovanie tiež môže vyžadovať viac výpočtov ako iné formy hashovania.
Hlavné rozdiely medzi týmito metódami sú, že lineárne hľadanie má najlepší cache výkon ale je najcitlivejšie na klastrovanie, kým dvojité hašovanie má nízky cache výkon ale nevykazuje v podstate žiadne klastrovanie; kvadratické hašovanie spadá v oboch oblastiach dostredu. Dvojité hašovanie tiež môže vyžadovať viac výpočtov ako iné formy hašovania.


Kritický vplyv na výkon hashovacej tabuľky s otvoreným adresovaním má ''koeficient zaťaženia''; pomer miest v poli, ktoré sa používajú. Ako sa zaťaženie zvyšuje k 100 %, stúpa dramaticky aj počet súšok, ktoré môžu byť potrebné na nájdenie alebo vloženie daného kľúča. Keď sa tabuľka zaplní, skúšacie algoritmy dokonca nemusia skončiť. Aj s dobrou hashovacou funkciou je koeficient zaťaženia zvyčajne obmedzený na 80 %. Nevhodná hashovacia funkcia môže vykazovať názky výkon aj pri nízkych zaťaženiach tým, že spôsobuje výrazné klastrovanie. Čo sposobuje, že hashovacie funkcie klastrujú nie je dobre známe a je ľahké neúmyselne napísať hashovaciu funkciu, ktorá spôsobuje ťažké klastrovanie.
Kritický vplyv na výkon hašovacej tabuľky s otvoreným adresovaním má ''koeficient zaťaženia''; pomer miest v poli, ktoré sa používajú. Ako sa zaťaženie zvyšuje k 100 %, stúpa dramaticky aj počet súšok, ktoré môžu byť potrebné na nájdenie alebo vloženie daného kľúča. Keď sa tabuľka zaplní, skúšacie algoritmy dokonca nemusia skončiť. Aj s dobrou hašovacou funkciou je koeficient zaťaženia zvyčajne obmedzený na 80 %. Nevhodná hašovacia funkcia môže vykazovať názky výkon aj pri nízkych zaťaženiach tým, že spôsobuje výrazné klastrovanie. Čo sposobuje, že hašovacie funkcie klastrujú nie je dobre známe a je ľahké neúmyselne napísať hašovaciu funkciu, ktorá spôsobuje ťažké klastrovanie.


== Otvorené adresovanie versus reťazenie ==
== Otvorené adresovanie versus reťazenie ==

Verzia z 16:42, 2. december 2005

Tento článok je čiastočný alebo úplný preklad článku neznámeho mena na neurčenej Wikipédii (číslo revízie nebolo určené). en:hash table Šablóna:Pracuje sa (2) Hašovacia tabuľka alebo hašovacia mapa alebo tabuľka výpočtu adresy transformáciou (kľúča) je v informatike údajová štruktúra, ktorá asociuje kľúče s hodnotami. Primárna efektívne podporovaná operácia je vyhľadávanie: pri zadaní kľúča (napr. meno osoby) nájsť zodpovedajúcu hodnotu (napr. telefónne číslo tejto osoby). Pracuje vďaka transformácii kľúča hašovacou funkciou na haš, číslo, ktoré tabuľka používa na nájdenie požadovanej hodnoty.

Malý telefónny zoznam ako hašovacia tabuľka.

Hašovacie tabuľky sa často používajú na implementáciu asociatívnych polí, množín a rýchlych vyrovnávacích pamätí (cache). Rovnako ako polia, hašovacie tabuľky poskytujú vyhľadávanie s konštantným časom O(1) v priemernom prípade, nezávisle na počte položiek v tabuľke. Avšak zriedkavý najhorší prípad môže byť až O(n). V porovnaní s inými údajovými štruktúrami asociatívnych polí sú hašovacie tabuľky najužitočnejšie pre uloženie veľkého množstva údajov.

Hašovacie tabuľky ukladajú údaje na pseudonáhodných miestach, preto prístup k údajom utriedeným spôsobom je veľmi časovo náročná operácia. Iné údajové štruktúry ako samovyvažovací binárny vyhľadávacie stromy sú vo všeobecnosti oveľa pomalšie (keďže zložitosť vyhľadávania majú O(log n)) a sú skôr zložitejšie na implementáciu ako hašovacie tabuľky, ale udržiavajú si neustále utriedenú štruktúru položiek. Pozri porovnanie hašovacích tabuliek a samovyvažovacích binárnych vyhľadávacích stromov.

Prehľad

Základné operácie, ktoré hašovacie tabuľky vo všeobecnosti podporujú sú:

vlož(kľúč, hodnota)
nájdi(kľúč) ktorá vráti hodnotu

Väčšina, ale nie všetky hašovacie tabuľky podporujú zmaž(kľúč). Môžu existovať aj iné služby ako iterovanie tabuľkou, zväčšenie tabuky, vyprázdnenie tabuľky. Niektoré tabuľky umožňujú uloženie viacerých hodnôt pod rovnakým kľúčom.

Kľúčom môže byť v podstate čokoľvek -- číslo, reťazec, objekt; niektoré tabuľky ukladajú dokonca aj odkaz na hodnotu. Jedinou požiadavkou je existencia hašovacej funkcie pre použité kľúče (pozri nižšie).

Hašovacie tabuľky používajú pole, ktoré sa tiež niekedy mätúco nazýva hašovacia tabuľka. Každý element poľa môže obsahovať jeden pár kľúč-hodnota nazývaný záznam.

Pretože počet platných kľúčov je zvyčajne oveľa väčší ako rozsah platných indexov do poľa, je potrebný spôsob konverzie každého kľúča na platný index. Toto vykonáva hašovacia funkcia, čo je funkcia, ktorá berie ako argument kľúč a vracia index do poľa. Indexovaný element poľa by zasa mal obsahovať záznam asociovaný s daným kľúčom.

Avšak preto, že existuje viac potenciálnych kľúčov ako indexov do poľa, možno dokázať (?Dirichletovym princípom), že dva alebo viac potenciálnych kľúčov budú mať rovnaký haš; toto sa nazýva kolízia. Hašovacia funkcia by mala byť navrhnutá tak, aby minimalizovala množstvo kolízií pre akýkoľvek vrátený index.

Keďže v jednom elemente poľa je možné uložiť iba jednu položku, musí byť použitá stratégia riešenia kolízií. Napríklad sa kolízny záznam uloží do nasledujúceho voľného elementu alebo môžu elementy poľa ukladať spojový zoznam záznamov.

Hoci sa nejaké kolízie vyskytnú, s dobrými hašovacími funkciami a tabuľkou plnou asi na 80 %, budú kolízie relatívne zriedkavé a výkon veľmi dobrý, keďže priemerne sa vykoná veľmi málo porovnaní. Avšak ak sa tabuľka príliš zaplní, výkon bude nízky a bude potrebné zväčšiť hašovaciu tabuľku. Zväčšenie tabuľky (Zmena veľkosti tabuľky) znamená, že sa budú musieť znova pridať všetky záznamy.

Väčšina implementácií hašovacích tabuliek nie sú perzistentnými údajovými štruktúrami v zmysle, že neexistuje spôsob ako ich aktualizovať a zároveň si udržať prístup k predchádzajúcej kópii hašovacej tabuľky (hoci sú perzistentné v zmysle uloženia na disku). Niektoré implementácie hašovacích tabuliek ako tie, ktoré používajú VList, sú perzistentné, ale v praxi sa zriedkavo používajú a nie sú také výkonné ako narmálne hašovacie tabuľky kvôli zvýšenému času na indexáciu.

Bežné využitie hašovacích tabuliek

Hašovacie tabuľky sa bežne využívajú pre tabuľky symbolov, rýchle vyrovnávacie pamäte a množiny.

Hašovacie tabuľky nie sú vo všeobecnosti perzistentnými údajovými štruktúrami v tom zmysle, že neexistuje jednoduchý a vzhľadom na úložný priestor efektívny spôsob, ako aktualizovať hašovaciu tabuľku a zároveň si si udržať prístup k jej predchádzajúcej kópii. Je to však možné implementovaním VListu, čím sa stane perzistentnou, ale nepriaznivo to ovplyvní výkon. hašovacie tabuľky môžu byť „perzistentné“ v inom zmysle - môžu byť uložené na disku. Databázové indexy bežne používajú diskové údajové štruktúry založené na hašovacích tabuľkách.

V počítačovom šachu sa hašovacia tabuľka bežne používa na implementáciu transpozičnej tabuľky.

Voľba dobrej hašovacej funkcie

Dobrá hašovacia funkcia má zásadný vplyv na výkon hašovacej tabuľky. Kolízie hašus sa vo všeobecnosti riešia určitou formou lineárneho vyhľadávania, takže ak hašovacia funkcia zvykne vracať podobné hodnoty, výsledkom bude pomalé vyhľadávanie.

Ideálna hašovacia funkcia by pri každej zmene jednotlivého bitu kľúča (vrátane rozšírenia a skrátenia kľúča) zmenila polovicu bitov hašu a táto zmena by bola nezávislá na zmenách spôsobených inými bitmi kľúča. Pretože môže byť ťažké navrhnúť dobrú hašovaciu funkciu, alebo ju výpočtovo náročné vykonávať, boli do veľkej miery skúmané stratégie riešenia kolízií, ktoré zmierňujú dopad slabého hašovacieho výkonu. Avšak žiadna z nich nie je taká efektívna ako využitie dobrej hašovacej funkcie.

Je žiadúce používať rovnakú hašovaciu funkciu pre polia akejkoľvek mysliteľnej veľkosti. Aby sme to dosiahli, index do poľa hašovacej tabuľky sa vo všeobecnosti počíta v dvoch krokoch:

  1. Vypočíta sa všeobecná hodnota hašu, ktorá zaplní veľkosť strojového celého čísla
  2. Táto hodnota sa redukuje na platný index poľa nájdením jeho modula s ohľadom na veľkosť poľa.

Veľkosti poľa hašovacej tabuľky sa niekedy volia tak, aby boli prvočíslami. Robí sa to, aby sme sa vyhli výskytu spoločných deliteľov hašu a veľkosti tabuľky, ktoré by boli príčinou kolízií po operácii modulo. Ale ani prvočíselná veľkosť tabuľky nie je náhradou za dobrú hašovaciu funkciu.

Bežnou alternatívou k prvočíselným veľkostiam je použitie veľkosti, ktorá je mocninou čísla dva a dosahovať operáciu modulo maskovaním bitov. Maskovanie bitov býva výpočtovo významne rýchlejšie ako operácia delenia. V každom prípade je dobrý nápad zariadiť, aby všeobecná hodnota hašu bola zostavená pomocou prvočísel, ktoré nezdieľajú spoločných deliteľov (napr. ako en:coprime) s dĺžkou tabuľky.

Prekvapivo bežným problémom, ktoý sa môže vyskytnúť pri hašovaní je klastrovanie. Klastrovanie sa vyskytuje, keď štruktúra hašovacej funkcie spôsobuje, že bežne používané kľúče padajú blízko vedľa seba alebo dokonca po sebe do hašovacej tabuľky. To môže spôsobiť významné zníženie výkonu ako sa tabuľka plní a používajú sa isté stratégie riešenia kolízií ako lineárne reťazenie.

Počas debugovania obsluhy kolízií v hašovacej tabuľke je niekedy užitočné použiť hašovacu funkciu, ktorá vždy vracia konštantnú hodnotu, napr. 1, ktorá spôsobí kolíziu po takmer každom vložení.

V prostrediach, kde sa záškodník môže pokúšať spomaliť algoritmus tým, že zadáva nepriaznivý vstup, býva dobrým riešením univerzálne hašovanie, schéma, kde náhodne volíme hašovaciu funkciu na začiatku algoritmu. Pretože záškodník nebude vedieť, akú hašovaciu funkciu používame, nebude vedieť určiť, ktorý vstup je nepriaznivý.

Riešenie kolízií

Ak hašovacia funkcia vráti pre dva rôzne kľúče rovnaký index, zodpovedajúce záznamy nemožno uložiť na to isté miesto. Keďže je už obsadené, musíme nájsť iné miesto pre uloženie záznamu a zabezpečiť, aby sme ho našli, keď ho neskôr vyhľadáme.

Pre predstavu dobrej stratégie riešenia kolízií si predstavte nasledovný výsledok odvodený použitím narodeninového paradoxu. Aj za predpokladu, že naša hašovacia funkcia vracia indexy rovnomerne rozložené v poli aj pre pole s 1 milónom záznamov, existuje 95% pravdepodobnosť aspoň jednej kolízie predtým, ako bude obsahovať 2500 záznamov.

Existuje niekoľko techník riešenia kolízií, ale najpoužívanejšími sú reťazenie a otvorené adresovanie.

Reťazenie

Kolízia hašu vyriešená reťazením.

V najjednoduchšej technike zreťazenej hašovacej tabuľky každé miesto v poli odkazuje na zreťazený zoznam vložených záznamov, pre ktoré hašovanie kľúča koliduje na rovnakú hodnotu. Pre vloženie je potrebné nájsť správne miesto a pridať ho na jeden koniec; pre mazanie je potrebn0 preh2ad8vanie zoznamu a odstránenie.

Zreťazené hašovacie tabuľky majú výhodu v porovnaní s tabuľkami s otvorenou adresáciou, že operácia odstránenia je jednoduchá a zmenu veľkosti tabľky je možné odkladať oveľa dlhšie, pretože výkon sa znižuje oveľa elegantnejšie aj ak je miesto obsadené. Vlastne mnohé zreťazené hašovacie tabuľky nepotrebujú zmenu veľkosti vôbec, pretože degradácia výkonu je lineárna so zaplňovaním tabuľky. Napríklad zreťazená hašovacia tabuľka obsahujúca dvojnásobok odporúčanej dátovej kapacity by bola priemerne iba asi dvakrát pomalšia ako rovnaká tabuľka s odporúčanou kapacitou.

Zreťazené hašovacie tabuľky dedia nevýhody spojových zoznamov. Ak sa ukladajú malé záznamy, režijné náklady spojového zoznamu môžu byť významné. Ďalšou nevýhodou je, že prechádzanie spojovým zoznamom má nízky cache výkon.

Namiesto zoznamov je možné pre reťazenie použíť alternatívne údajové štruktúry. Napríklad použitím samovyvažovacieho stromuje možné znížiť teoretický čas najhoršieho prípadu z O(n) na O(log n). Ale preto, že každý zoznam by mal byť krátky, tento prístup zvyčajne nevedie k zvýšeniu výkonu, iba ak by bola tabuľka navrhnutá na fungovanie pri plnej kapacite alebo vykazovala nazvyčajne vysoké množsto kolízií. Je tiež možné použiť dynamické polia pre zníženie režijných nákladov a zvýšenie výkonnosti cache pri malých záznamoch.

Niektoré implementácie reťazenia používajú optimalizáciu pri uložení prvého záznamu každej reťaze v tabuľke. Hoci to môže zvýšiť výkon, vo všeobecnosti sa to neodporúča: zreťazené tabulľky s rozumnými hodnotami zaťaženia obsahujú veľký pomer voľného miesta a väčšia veľkosť miesta spôsobuje väčšie mrhanie miestom.

Otvorené adresovanie

Haš collision resolved by linear probing (interval=1).

Hašovacie tabuľky s otvoreným adresovaním môžu ukladať záznamy priamo v poli. Kolízia hašu sa rieši skúšaním - prehľadávaním alternatívnych miest v poli (skúšacia sekvencia), pokým sa buď nenájde cieľový záznam alebo voľné miesto, čo indikuje, že sa v tabuľke dený kľúč nenachádza. Medzi známe skúšacie sekvencie patria:

lineárne hľadanie
v ktorom je interval medzi skúškami fixný -- často 1,
kvadratické skúšanie
v ktorom interval medzi skúškami rastie lineárne (takže indexy sú opísané kvadratickou funkciou),
dvojité hašovanie
v ktorom je interval medzi skúškami fixný pre každý záznam, ale počíta sa inou hašovacou funkciou

Hlavné rozdiely medzi týmito metódami sú, že lineárne hľadanie má najlepší cache výkon ale je najcitlivejšie na klastrovanie, kým dvojité hašovanie má nízky cache výkon ale nevykazuje v podstate žiadne klastrovanie; kvadratické hašovanie spadá v oboch oblastiach dostredu. Dvojité hašovanie tiež môže vyžadovať viac výpočtov ako iné formy hašovania.

Kritický vplyv na výkon hašovacej tabuľky s otvoreným adresovaním má koeficient zaťaženia; pomer miest v poli, ktoré sa používajú. Ako sa zaťaženie zvyšuje k 100 %, stúpa dramaticky aj počet súšok, ktoré môžu byť potrebné na nájdenie alebo vloženie daného kľúča. Keď sa tabuľka zaplní, skúšacie algoritmy dokonca nemusia skončiť. Aj s dobrou hašovacou funkciou je koeficient zaťaženia zvyčajne obmedzený na 80 %. Nevhodná hašovacia funkcia môže vykazovať názky výkon aj pri nízkych zaťaženiach tým, že spôsobuje výrazné klastrovanie. Čo sposobuje, že hašovacie funkcie klastrujú nie je dobre známe a je ľahké neúmyselne napísať hašovaciu funkciu, ktorá spôsobuje ťažké klastrovanie.

Otvorené adresovanie versus reťazenie