Sunteți pe pagina 1din 30

1

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.

Astfel, cautarea intr‐o tabela de dispersie se face in doi pasi:


(1) se aplica functia de dispersie asupra cheii de cautare si se determina astfel indexul;
(2) Se acceseaza elementul de la indexul respectiv in tabela si se verifica daca acolo este
stocat elementul cautat sau nu. Se observa ca fiecare din cei doi pasi se realizaeaza intr‐un
numar constant de operatii, adica, operatia de cautare are o complexitate timp O(1).

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.

Obs: Dispersia perfecta nu poate fi obtinuta in practica.

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.

Observam si ca valoarea lui M (numarul de pozitii in tabela, adica dimensiunea tabelei)


influenteaza numarul de coliziuni.

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)

Stergerea din tabela va ramane ca exercitiu!

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).

In schimb, folosesc memorie suplimentara pentru stocarea informatiei de inlantuire pentru


fiecare element al listelor liniare. De asemenea, au dezavantajul discutat la listele inlantuite
allocate dinamic, si anume ca nu permit exploatarea unor strategii de caching (elementele
listei nu sunt stocate in zone contigue de memorie).

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.

Evident ca la cautare se aplica aceeasi strategie: se testeaza elementul de pe pozitia h1;


daca nu este cel cautat, se testeaza pozitia h2, 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.

Obs.: In cazul probing‐ului se foloseste o singura functie de dispersie pe baza careia se


calculeaza un index initial. De aici incepe testarea altor pozitii calculate pe baza indexului
initial dupa o anumita regula.

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)

Dezavantajele vin din faptul ca se introduce un nou parametru ce influenteaza


performantele tabelei (dimensiunea zonei de overflow). In plus, o pozitie din tabela ocupa
dimensiunea unui element, chiar daca este goala.

Obs.: Zona de overflow poate fi stocata si intr‐un tablou diferit.

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).

Pregatiti o lista de intrebari/nelamuriri legate de tabelele de dispersie la care voi incerca sa


va raspund la curs.
Aceasta prelegere nu va fi reluata in timpul cursului!

55
56
57

S-ar putea să vă placă și