Documente Academic
Documente Profesional
Documente Cultură
In aceasta prelegere va voi prezenta o structura de date foarte populara, deoarece este
utilizata pentru a implementa in mod eficient operatia de cautare intr‐un set de date, si
anume Tabela de Dispersie. Termenul in limba engleza este Hash Table, sau Hash Map.
Este foarte posibil ca voi sa va mai fi intalnit cu termenul de HASH, cel mai probabil in
contextul asa numitelor checksums (sume de verificare) folosite in verificarea integritatii
datelor digitale, spre exemplu la identificarea erorilor ce pot aparea la transmisia sau
stocarea datelor.
Poate unii dintre voi au mai auzit si termenul de functii hash criptografice, care dupa cum le
spune si numele sunt folosite in securitatea comunicatiilor, si nu sunt altceva decat niste
algoritmi matematici care realizeaza maparea intre un mesaj de lungime arbitrara si un sir
de biti de dimensiune fixa.
29
Tabela de dispersie este un tip abstract de date care foloseste, intr‐o anumita masura, un
concept similar pentru a implementa eficient cautarea unei inregistrari intr‐o colectie de
date de tip dictionar. Cand spunem ca implementam eficient operatia de cautare,
intelegem ca aceasta operatie va avea o complexitate timp mica si vom vedea ca, in
anumite conditii, tabelele de dispersie permit regasirea unei inregistrari in timp constant,
O(1).
Operatiile de baza care se definesc pentru acest tip abstract de date sunt Inserarea
(adaugarea unei inregistrari in dictionar), Cautarea (regasirea unei inregistrari) si Stergerea
(inlaturarea unei inregistrari din dictionar).
30
Tabelele de dispersie sunt de doua tipuri: tabele fixe, sau inchise, si tabele dinamice, sau
deschise. Tabelele inchise pot incorpora un numar fix de inregistrari ale unui dictionar, iar
daca dictionarul depaseste aceasta dimensiune, este necesara realocarea tabelelor. In
schimb, cele deschise permit adaugarea dinamica de inregistrari, teoretic, fara a fi necesara
o realocare atunci cand numarul de elemente depaseste dimensiunea tabelei. Totusi, vom
vedea ca, la un moment dat, cresterea numarului de elemente poate degrada performanta
la cautare a tabelei si va fi necesara realocarea si incazul tabelelor deschise.
31
Pentru implementarea eficienta a operatiei de cautare, tabelele de dispersie folosesc o asa‐
numita functie de dispersie (functie hash) care transforma cheia de cautare asociata unei
inregistrari intr‐un numar, o asa‐numita amprenta. Aceasta amprenta se numeste valoare
hash si este folosita drept index in tabela la care se va regasi inregistrarea respectiva din
dictionar.
32
Tabela de dispersie este, in esenta, un tablou (un vector), sa zicem de dimensiune M, in
care sunt stocate inregistrarile.
Intr‐un tablou clasic (standard), daca vrem sa cautam un element trebuie sa realizam o
parcurgere a sa, index cu index, pentru a compara fiecare element cu cel cautat. In cel mai
bun caz, tabloul este sortat si atunci putem implementa cautarea binara (complexitate
O(logN)).
Spre deosebire de tabloul standard, tabela de dispersie foloseste un algoritm de hashing
pentru a stabili indexul in tablou in care ar trebui sa se stocheze (deci si sa se regaseasca) o
inregistrare. Adica, indexul in tablou este determinat aplicand functia de dispersie asupra
cheii de cautare asociata unei inregistrari. Amprenta calculata folosind functia de dispersie
va reprezenta indexul in tablou.
33
Modul in care functia de dispersie calculeaza valoarea hash este foarte important sub doua
aspecte:
(1) cat de rapid se calculeaza valoarea hash pe baza unei chei
(2) pentru cate chei functia de dispersie asociaza o aceeasi valoare hash
Comportamentul functiei hash influenteaza semnificativ performanta la cautare a tabelei
de dispersie.
34
Teoretic, atunci cand apar coliziuni (functia de dispersie calculeaza aceeasi valoare hash
pentru doua sau mai multe chei), inregistrarile respective ar trebui stocate in tabela la
acelasi index (valoarea hash). Cele doua tipuri de tabele de dispersie (deschise/inchise)
trateaza in mod diferit stocarea si regasirea acestor chei.
35
36
Exista multe tipuri de functii hash, dar unele din cele mai utilizate in practica sunt cele
modulare, cele bazate pe metoda inmultirii si cele cu deplasari pe biti.
37
38
39
Comparam aplicarea a doua tipuri de functii hash (modulara si cu metoda inmultirii) pe un
dictionar de N=10 chei formate din siruri de caractere.
Se observa ca pentru o tabela cu M=7 pozitii functia ce foloseste metoda inmultirii produce
o dispersie mai buna decat metoda modulara.
40
Cele doua tipuri de tabele de dispersie (deschise/inchise) trateaza in mod diferit coliziunile.
In tabelele deschise, fiecare pozitie din tabela va reprezenta adresa primului element al
unei liste liniare simplu inlantuite. In lista stocata la fiecare
pozitie din tabela se vor regasi toate inregistrarile carora functia de dispersie le asociaza
aceeasi valoare hash (adica toate sinonimele).
Astfel, tabela de dispersie este reprezentata sub forma unui tablou de pointeri.
41
Initializarea tabelei presupune ca pe fiecare pozitie din tabela sa inregistram un pointer
null. Rezultatul corespunde situatiei unei tabele fara nicio inregistrare.
Cautarea unui element cu o cheie data se face urmarind cei doi pasi precizati anterior:
(1) Calcularea indexului din tabela unde ar trebui sa fie stocat elementul (valoarea hash)
aplicand functia de dispersie
(2) Accesarea locatiei respective din tabela. In tabelele deschise, la aceasta locatie se
gaseste o lista liniara simplu inlantuita. Aceasta lista va fi parcursa pentru a determina daca
cheia cautata se afla printre elementele listei.
Daca elementul cautat nu se afla in lista, functia va returna 0. Altfel, functia de cautare va
returna adresa din memorie unde este stocat elementul cu cheia de cautare data.
42
Pentru adaugarea unei inregistrari cu o cheie 'key' in dictionar, se aplica tot doi pasi:
(1) Se calculeaza indexul din tabela la care trebuie stocata inregistrarea (aplicand functia de
dispersie)
(2) Daca inregistrarea nu se afla deja in tabela, ea va fi inserata in lista inlantuita stocata la
indexul determinat in pasul 1. Inserarea inregistrarii se face in fata listei deoarece
presupune un nr constant de operatii ‐ O(1) (inserarea la sfarsit ar presupune parcurgerea
listei)
Cazul in care inregistrarea exista deja in lista poate fi tratat in mai multe moduri:
‐ se poate genera mesaj de eroare
‐ se poate ignora inserarea
‐ se poate suprascrie inregistrarea (corespunzatoare unei actualizari)
43
Tabelele deschise (dinamice) au avantajul ca pot fi bazazte pe functii de dispersie simple
(spre deosebire de tabelele inchise unde se folosesc mai multe functii pentru tratarea
coliziunilor).
44
Atunci cand proiectam solutii de manipulare a seturilor de date folosind tabele de
dispersie, este important sa ne asiguram ca tabela utilizata va furniza performantele
asteptate.
Exista doi factori care influenteaza major performanta la cautare:
(1) dimensiunea tabelei (de obicei dimensiunea tabelei < nr. de element din setul de date)
(2) numarul de coliziuni produse de functia de dispersie si uniformitatea dispersiei
(imprastierea uniforma a coliziunilor pe multimea indecsilor)
Pentru a asigura performante bune unei tabele de dispersie se pot intreprinde doua tipuri
de evaluari:
‐ in faza de proiectare (de alegerea a dimensiunii tabelei si a functiei de dispersie) ‐ se
estimeaza factorul de incarcare
‐ in faza de testare (dupa ce tabela a fost populata cu date) ‐ se masoara gradul de
clusterizare
Daca gradul de clusterizare nu este satisfacator, se revine la faza de proiectare si se variaza
dimensiunea tabelei, testand si cu alte functii de dispersie.
45
46
Se observa ca cel mai bun grad de clusterizare se obtine pentru a 3‐a varianta (M=7,
dispersie prin metoda inmultirii). Totusi, exemplul este unul minimalist.
Este interesant de evaluat influenta dimensiunii tabelei (valoarea lui M) asupra gradului de
clusterizare si pentru metoda inmultirii. De asemenea, este interesant de facut aceasta
evaluare pe seturi de date mai consistente. Veti face astfel de experimente la laborator.
47
In cazul tabelelor inchise, coliziunile se pot rezolva prin mai multe metode discutate in
continuare. Este important de retinut ca, in cadrul unei tabele inchise, elementele din
dictionar sunt stocate direct in tabela, spre deosebire de cele deschise unde sunt stocate
doar adresele unor liste liniare simplu inlantuite.
Atunci cand se produce o coliziune (spre exemplu la inserarea unui element constatam ca
pozitia pe care acest element ar trebui sa se regaseasca in tabela este deja ocupata), va
trebui sa calculam un nou index in tabela, dar in limitele [0, M‐1].
Spre deosebire de tabelele deschise care, teoretic, pot stoca un numar nelimitat de
elemente, tabelele inchise au un numar limitat de 'locuri'. Atunci cand toate aceste 'locuri'
devin ocupate, este necesara realocarea tabelei si transferarea continutului existent in
noua tabela. Evident, functia de dispersie va produce noi indecsi pentru elementele
existente (deoarece valoarea hash depinde de nr. M) care vor fi astfel 'imprastiate‘ diferit in
tabela.
48
49
De exemplu, pentru linear probing:
‐ se testeaza intai indexul h returnat de functia h(x); daca acesta este deja ocupat (cu un
sinonim), va trebui sa gasim un alt index la care sa stocam inregistrarea;
‐ testam indexul h1 returnat de h1(x), unde h1 va fi (h+c*1)%M (daca c=1); acest lucru
este echivalent cu testarea urmatorului index (h+1); daca si acesta este ocupat
se aplica h2(x), s.a.m.d.
Daca dupa aplicarea tuturor functiilor hash disponibile, tot nu se gaseste o pozitie valida pt
inserarea inregistrarii si va initia realocarea tabelei.
50
51
52
Ideea abordarii de tip re‐hashing este de a folosi o lista predefinita de functii hash care sa
se aplice in ordine. Aceste functii pot fi diferite intre ele (spre ex., h1=dispersie modulara,
h2=dispersie pin inmultire, etc.).
53
Functia de dispersie genereaza indecsi doar in intervalul primar. Daca se produce o
coliziune, elementul va fi stocat intr‐o pozitie din zona de overflow. Aceasta pozitie/adresa
este retinuta la indexul initial din zona primara. Practic, metoda este similara cu abordarea
prin inlantuire (de la tabele deschise), doar ca elimina cateva din dezavantaje:
‐ memoria este pre‐alocata (mai rapid decat alocarea dinamica)
‐ se pot exploata mecanismele de caching (tabela ocupa o zona contigua de memorie)
54
Va incurajez sa explorati si sa intelegeti mai bine diferitele aplicatii ce folosesc tabele hash,
accesand link‐urile furnizate in urmatoarele slide‐uri si nu numai.
Pentru urmatorul curs va invit sa pregatiti o scurta prezentare a unei aplicatii practice de
utilizare a tabelelor hash sau a unui studiu de caz practic descriind analiza
performantelor obtinute implementand tabela hash cu diferite valori ale parametrilor
(poate fi gasit pe internet sau efectuat de voi).
55
56
57