POZOR klíč NENÍ index (adresa), ale podle klíče se hodnoty ukládají na indexy (adresy).

Podobné dokumenty
Datové struktury 2: Rozptylovací tabulky

Návrh designu: Radek Mařík

Základy algoritmizace. Hašování

Vyhledávání, zejména rozptylování

HASHING GENERAL Hashovací (=rozptylovací) funkce

Rozptylovací tabulky

Třetí skupina zadání projektů do předmětu Algoritmy II, letní semestr 2017/2018

Úvod. Úvod do programování. Úvod. Hashovací tabulky

2 Datové struktury. Pole Seznam Zásobník Fronty FIFO Haldy a prioritní fronty Stromy Hash tabulky Slovníky

HASHING GENERAL Hashovací (=rozptylovací) funkce

vyhledávací stromové struktury

5 Rekurze a zásobník. Rekurzivní volání metody

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Adresní vyhledávání (přímý přístup, zřetězené a otevřené rozptylování, rozptylovací funkce)

Kolekce, cyklus foreach

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

Dynamické datové struktury IV.

1. Téma 12 - Textové soubory a výjimky

boolean hasnext() Object next() void remove() Kolekce

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

Topics. Vyhledávání, zejména rozptylování. Slovník - Dictionary. Vyhledávání. Rozptylování - Hashing. Rozptylování - Hashing

Hašování (Vyhledávání metodou transformace klíče)

Lineární spojový seznam (úvod do dynamických datových struktur)

vyhledávací stromové struktury

Algoritmy a datové struktury

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Algoritmizace Hashing II. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

Algoritmizace a programování

Šablony, kontejnery a iterátory

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Dynamické datové struktury I.

součet cvičení celkem. známka. Úloha č.: max. bodů: skut. bodů:

Prioritní fronta, halda (heap), řazení

Konstruktory a destruktory

Abstraktní datové typy

Datové struktury. alg12 1

Programování v C++, 2. cvičení

Java a XML. 10/26/09 1/7 Java a XML

Náplň. v Jednoduché příklady na práci s poli v C - Vlastnosti třídění - Způsoby (algoritmy) třídění

7. Dynamické datové struktury

Databázové systémy. - SQL * definice dat * aktualizace * pohledy. Tomáš Skopal

Abstraktní datové typy FRONTA

Standardní algoritmy vyhledávací.

Výčtový typ strana 67

Fronta (Queue) Úvod do programování. Fronta implementace. Fronta implementace pomocí pole 1/4. Fronta implementace pomocí pole 3/4

Metodický koncept k efektivní podpoře klíčových odborných kompetencí s využitím cizího jazyka ATCZ62 - CLIL jako výuková strategie na vysoké škole

Programování v Javě I. Leden 2008

Microsoft. Word. Hromadná korespondence. Mgr. Jan Veverka Střední odborná škola sociální Evangelická akademie

Šablony, kontejnery a iterátory

Programování v Javě I. Únor 2009

Semestrální práce 2 znakový strom

Typický prvek kolekce pro české řazení

Spojový seznam. Jan Kybic.

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

Algoritmy II. Otázky k průběžnému testu znalostí

Jazyk C# a platforma.net

Algoritmizace řazení Bubble Sort

7. Datové typy v Javě

ADT/ADS = abstraktní datové typy / struktury

Obrázek. Základní popis, zadání úkolu. Struktura tříd,

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Lineární datové struktury

Základní pojmy. Úvod do programování. Základní pojmy. Zápis algoritmu. Výraz. Základní pojmy

Úvod do programovacích jazyků (Java)

Jazyk C# (seminář 3)

Algoritmizace a programování

DSA, První krok: máme dokázat, že pro left = right vrátí volání f(array, elem, left, right)

Robert Haken [MVP ASP.NET/IIS, MCT] software architect, HAVIT, Základní algoritmy v praxi

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

Prohledávání do šířky = algoritmus vlny

SPJA, cvičení 1. ipython, python, skripty. základy syntaxe: základní datové typy, řetězce. podmínky: if-elif-else, vyhodnocení logických výrazů

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Časová a prostorová složitost algoritmů

Abstraktní datové typy: zásobník

Algoritmy a datové struktury

Úvod Přetěžování Generika Kolekce Konec. Programování v C# Další jazykové konstrukce. Petr Vaněček 1 / 31

Stromy. Jan Hnilica Počítačové modelování 14

Správa paměti. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

Red Black strom (Red Black Tree) Úvod do programování. Rotace. Red Black strom. Rotace. Rotace

Programování v Pythonu

9. přednáška - třídy, objekty

Dědění, polymorfismus

Vyhledávací algoritmy

KTE / ZPE Informační technologie

Tabulka. Často jde nejenom o dynamickou množinu, ale i statickou množinu. Matematické tabulky statická množina

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Implementace LL(1) překladů

Úvod do programovacích jazyků (Java)

DobSort. Úvod do programování. DobSort Implementace 1/3. DobSort Implementace 2/3. DobSort - Příklad. DobSort Implementace 3/3

KMI / TMA Tvorba mobilních aplikací. 6. seminář ZS 2016/2017 Středa 13:15-15:45

IAJCE Přednáška č. 9. int[] pole = new int[pocet] int max = pole[0]; int id; for(int i =1; i< pole.length; i++) { // nikoli 0 if (Pole[i] > max) {

Rozklad problému na podproblémy

17. Projekt Trojúhelníky

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Spojová implementace lineárních datových struktur

Spojové struktury. Jan Faigl. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze. Přednáška 10 A0B36PR1 Programování 1

Spojové struktury. Spojová struktura (linked structure):

Generické programování

Transkript:

Hashovací tabulka = Hash table Hashovací tabulka (hash table, rozptýlená tabulka, hešovací tabulka) je datová struktura, která slouží k ukládání dvojic klíč-hodnota. kombinuje výhody vyhledávání pomocí indexu (složitost O(1)) a procházení seznamu (nízké nároky na paměť). konstantní složitost rozloží prvky rovnoměrně POZOR klíč NENÍ index (adresa), ale podle klíče se hodnoty ukládají na indexy (adresy). Podporované funkce vkládání mazání vyhledávání modulo = typická hash fce h(k) = k mod m Možnosti ukládání dvojic klíč-hodnota Pole Složitost O(1), protože je zde přímé adresování prvku. Polem můžeme implementovat rychle, ale nevýhodou je prostorová náročnost. Uvažujme, že chceme ukládat dvojice klíč-hodnota a vyhledávat v nich. Jednou z možností by pak bylo jako klíč zvolit celé číslo (nebo nějaké mapování klíče na celé číslo) a hodnoty ukládat do pole. Tímto bychom si sice zajistili, že prvek nalezneme s konstantní paměťovou složitostí (jednoduše bychom jej vyžádali pomocí indexu), ale na druhou stranu by docházelo k obrovskému mrhání pamětí. Spojový seznam Opačným přístupem by bylo využití seznamu, ve kterém bychom vyhledávali sekvenčně. Zcela zřejmě by tímto odpadl problém s nevyužitými klíči a pamětí (nevyužité klíče by vůbec neexistovaly). Nevýhoda je ovšem zcela zřejmá výkonnost. V okamžiku, kdy bychom se neptali jen tisícovky respondentů (lokální výzkum), ale uspořádali bychom globální anketu s miliony dotazovaných, tak by v těchto datech již nešlo rozumně vyhledávat (pro nalezení jednoho záznamu bychom spotřebovali O(n) kroků), protože musí prohledat vše od prvního.

Hashovací (rozptýlená) tabulka Hashovací (rozptýlená) tabulka je struktura, jež je postavena nad polem omezené velikosti n (tzn. pole nepopisuje celý stavový prostor klíče), a která pro adresaci využívá hashovací funkci. Nalezení prvku pro daný klíč zabere průměrně O(1) operací. Hashovací (rozptylovací) funkce Hashovací funkce má následující vlastnosti: nezaručuje, že pro dva různé objekty vrátí různou adresu = vzniká kolize lze řešit několika způsoby (viz dále) využívá celého prostoru adres se stejnou pravděpodobností, tzn. rovnoměrné rozložení prvků. výpočet adresy proběhne velmi rychle. pomocí rozptylovací fce zredukujeme potřebné místo. půměrná složitost je konstantní O(1) h(k) = k mod m (k je klíč, m je velikost pole) Synonymum = prvky mají stejný výsledek funkce h 1 (k). Kolize Jednou z uvedených vlastností hashovací funkce je, že nezaručuje, že dvěma různým objektům nepřiřadí stejné adresy. Situaci, kdy chceme uložit na stejnou adresu více objektů, se říká kolize. Kolize = dva nebo více klíčů (synonum) se zobrazují na tutéž pozici v tabulce. Odstranění kolizí zřetězené hashování = řetězení klíčů (synonym) do pomocných datových struktur (spojový seznam, vyhledávací strom) otevřené adresování = ukládání synonym (klíčů) přímo na indexy tabulky, které jsou vypočteny pomocí další funkce (rozptylovací, hash fce) o linear probing (lineární prohledávání) o lineární prohledávání s krokem o double hashing o kvadratické hashování Zřetězené hashování nejjednodušší způsob odstranění kolizí hodnoty ukládáme do spojových seznamů nastane-li kolize, prvek se pouze přidá na konec adresovaného spojového seznamu nevýhoda při velkém zaplnění tabulky dojde k rychlé degradaci výkonu kvůli sekvenčnímu prohledávání příslušných seznamů. Otevřené hashování (rozptylování, adresování) hodnoty jsou ukládány přímo do pole. klíče jsou unikátní. má dva parametry klíč a číslo pokusu kolidující prvky se neřetězí, ale ukládají se do další volné pozice v hlavním poli. při mazání je potřeba pozici prvku označit značkou (např. DELETED nebo "null") nebo zbytek "řetězce" posunout dopředu (vlevo). u VKLÁDÁNÍ lineární složitost, musí se nejprve projít všechny "chlívečky" značku DELETED fce prohledávání ignoruje, fce VKLÁDÁNÍ přepisuje PŘ - vkládám hodnoty 1, 3, 9, 11 a m = 4 (= jsou 4 indexy mod 4) Zřetězené hashování Otevřené hashování 0 0 1 2 3 1 1, 9 2 11 1 9 3 3 3, 11

Existují tři základní strategie, pomocí nichž se tabulka vypořádává s kolizemi linear probing, linear probing s krokem a double hashing. Linear probing (otevřené hashování s lineárním prohledáváním) nejprve vypočteme adresu, na kterou daný prvek uložíme je-li adresa obsazená, tak se posuneme o jedno místo dál a zkusíme prvek uložit znovu postup se opakuje dlouho, dokud se nám prvek nepodaří uložit. Ukládací schéma lze charakterizovat funkcí ( i {0, 1,..., n/1): ): adresa = h(k, m) + i mod m PŘ mod 13, bude pole o indexech 0 12 0 1 2 3 4 5 6 7 8 9 10 11 12 4 17 19 5 - vkládám 4 = na i = 4, protože 4 mod 13 = 0, zbyde 4 - vkládám 19 = na i = 6, protože 19 mod 13 = 1, zbyde 6 - vkládám 17 = na i 4, protože 17 mod 13 = 1, zbyde 4 KOLIZE!!! vložím na nejbližší volný i = 5 - vkládám 5 = na i 5, protože 5 mod 13 = 0, zbyde 5 KOLIZE!!! vložím na nejbližší volný i = 7 Vyhledávání prázdného indexu hledám např. 17 (= 13 + 4). Začnu hledat od první možné pozice i = 4 a hledám postupně směrem vpravo, dokud nenajdu prázdný index. Linear probing s krokem zvolíme si hodnotu pevného kroku hodnota kroku musí být nesoudělná s m (velikost pole) Shlukování (= clustering) Velkou nevýhodou linear probingu je shlukování. Vzhledem k principu ukládání objektů dochází ke vzniku shluků objektů, jež mají blízkou nebo totožnou adresu. Tyto shluky je pak při vyhledávání nutné sekvenčně procházet, což má dopad na výkon. Ten je ještě vyšší než u zřetězeného rozptylování, protože shluky mohou obsahovat prvky odpovídající více klíčům. Mazání prvků mazaný prvek se označí značkou (DELETE, "null" apod.) tím vznikne objekt, který značí, že je dané místo prázdné operace vyhledávání null objekt přeskakuje operace vkládání null objekt přepíše, tzn. uloží na toto místo nový prvek. je při mazání možné prvek odstranit všechny prvky ve zbylé části shluku (tj. po nejbližší null) je ale nutno re-uložit, tzn. re-hashovat. není možné prvky pouze "setřepat", protože bychom mohli ztratit hodnoty pro další klíče, které se v daném shluku vyskytují (pokud bychom z tabulky na obrázku smazali prvek C a zbytek shluku setřepali, tak již nikdy nenalezneme prvek A) POZOR nutno nejprve zjistit, co patří do shluku!!! Double hashing eliminuje shlukování díky využití dvojice rozptylovacích funkcí h 1 (k) a h 2 (k) klasická fce h 1 (k) = k mod m vypočte iniciální adresu stejným způsobem jako u linear probingu nebo zřetězeného rozptylování fce h 2 (k) určí velikost kroku h 2 (k) = k mod m + 1 a její výsledek musí být nesoudělný s "m" funkce h 2 (k) nastoupí, pokud je dané místo již obsazené, a vypočte posun pokud je i nové místo plné, dojde opět k posunu na základě funkce h 2 (k) POZOR u fce h 2 (k) nutno zajistit nesoudělnost hodnoty m a velikost kroku adresa = h 1 (k) + h 2 (k)

PŘ 0 1 2 3 4 5 6 7 8 9 10 5 16 6 mod 11 = máme 11 indexů h 1 (k) = k mod 11 (= k %11) h 2 (k) = k mod 4 + 1 hash 5, 16, 6 Mazání prvků Při mazání prvků si tentokrát již nemůžeme pomoci novým uložením zbytku shluku, protože double hashing shluky netvoří, resp. tvoří je výrazně méně. Musíme proto striktně využívat již zmíněného null objektu, kterým nahradíme mazaný objekt. Hashovaci tabulka @param <KEY> typovy parametr klice @param <VALUE> typovy parametr hodnoty public class HashTable<KEY, VALUE> { //Pomer zaplneni pri kterem dojde k vytvoreni nove (vetsi) tabulky private final float LOAD_FACTOR = 0.75f; //Pomer zaplneni pri kterem dojde k vytvoreni nove (mensi tabulky) private final float COLLAPSE_RATIO = 0.1f; //Hodnota, pod kterou nikdy neklesne velikost tabulky private final int INITIAL_CAPACITY; //Pocet ulozenych prvku private int size = 0; private Entry<KEY, VALUE>[] table; Zkonstruuje hashovaci tabulku s vychozi kapacitou 10 public HashTable() { this(10); //vychozi kapacita Zkonstruuje hashovaci tabulku @param initialcapacity kapacita, pod kterou nikdy tabulka neklesne public HashTable(int initialcapacity) { if (initialcapacity <= 0) { throw new IllegalArgumentException("Kapacita nesmi byt nulova nebo zaporna"); this.initial_capacity = initialcapacity; this.table = new Entry[initialCapacity]; Vlozi prvek do tabulky, pokud jiz prvek s danym klicem existuje, tak bude nahrazen @param value hodnota @return @null pokud klic v tabulce neexistuje, v opacnem pripade nahrazena hodnota @throws IllegalArgumentException pokud je klic @null public VALUE put(key key, VALUE value) { if (key == null) { throw new IllegalArgumentException("Klic nesmi byt null"); VALUE val = performput(key, value);

if (val == null) { size++; resize(); return val; Odstrani prvek odpovidajici danemu klici @return @null pokud klic v tabulce neexistuje, v opacnem pripade odstranena hodnota public VALUE remove(key key) { Entry<KEY, VALUE> e = getentry(key); if (e == null) { //prvek neexistuje VALUE val = e.value; e.key = null; //prvek je nyni sentinelem e.value = null; size--; resize(); return val; //odstranime referenci na hodnotu, aby GC mohl cinit svou praci //vratime puvodni hodnotu Navrati hodnotu asociovanou s danym klicem @return hodnota, @null pokud neni v tabulce obsazena public VALUE get(key key) { Entry<KEY, VALUE> e = getentry(key); if (e == null) { return e.value; Dotaz na pritomnost prvku s danym klicem @return @true, pokud tabulka obsahuje hodnotu asociovanou s danym klicem, @false v opacnem pripade public boolean contains(key key) { return getentry(key)!= null; Dotaz na pocet ulozenych prvku @return pocet ulozenych prvku public int size() { return size; Kolekce vsech ulozenych hodnot @return kolekce ulozenych hodnot (poradi neni zadnym zpusobem zaruceno) public Collection<VALUE> values() { List<VALUE> values = new ArrayList<VALUE>(size); for (int i = 0; i < table.length; i++) { if (table[i]!= null && table[i].key!= null) { values.add(table[i].value); return values; Kolekce vsech klicu @return kolekce vsech klicu, ktere se vyskytuji v tabulce public Collection<KEY> keys() { List<KEY> keys = new ArrayList<KEY>(size);

for (int i = 0; i < table.length; i++) { if (table[i]!= null && table[i].key!= null) { keys.add(table[i].key); return keys; Vrati zaznam, ktery odpovida danemu klice @return private Entry getentry(key key) { int index = key.hashcode() % table.length; //dokud nenarazime na volne misto, existujici zaznam pro klic nebo sentinel while (table[index]!= null) { if (key.equals(table[index].key)) { //zaznam existuje return table[index]; index = (index + 1) % table.length; //prejdeme na dalsi adresu //nenalezen Provede samotnou operace vlozeni, aniz by jakkoliv menil informaci o velikosti pole (size), pripadne menil velikost pole (resize) Je-li klic jiz v tabulce obsazen, tak bude asociovana hodnota nahrazena @param value hodnota @return byl-li klic v tabulce jiz obsazen, tak nahrazena hodnota, v opacnem pripade @null private VALUE performput(key key, VALUE value) { Entry<KEY, VALUE> e = getentry(key); if (e!= null) { //prvek je v tabulce VALUE val = e.value; e.value = value; //zamenime hodnoty return val; int index = key.hashcode() % table.length; while (table[index]!= null && table[index].key!= null) { //dokud nenarazime na prazdne misto nebo sentinel index = (index + 1) % table.length; //posuneme se o adresu dal if (table[index] == null) { //prazdne misto table[index] = new Entry<KEY, VALUE>(); table[index].key = key; table[index].value = value; Vypocte velikost, jakou by mela tabulka mit @return velikost, jakou by mela tabulka mit private int calculaterequiredtablesize() { if (this.size() / (double) table.length >= LOAD_FACTOR) { //tabulka je preplnena return table.length * 2; else if (this.size() / (double) table.length <= COLLAPSE_RATIO) { //vratime vetsi z hodnot SOUCASNA_VELIKOST/2 a INITIAL_CAPACITY return Math.max(this.INITIAL_CAPACITY, table.length / 2); else { return table.length; //tabulka ma spravnou velikost

Zmeni velikost tabulky, je-li to nutne private void resize() { int requiredtablesize = calculaterequiredtablesize(); if (requiredtablesize!= table.length) { //pokud je treba zmenit velikost tabulky Entry<KEY, VALUE>[] oldtable = table; table = new Entry[requiredTableSize]; //tak vytvorime novou tabulku for (int i = 0; i < oldtable.length; i++) { if (oldtable[i]!= null && oldtable[i].key!= null) { this.performput(oldtable[i].key, oldtable[i].value); //a hodnoty do ni ulozime Vnitrni trida reprezentujici zaznam tabulky private class Entry<KEY, VALUE> { //Klic, @null == prvek je sentinel private KEY key; //Hodnota private VALUE value;