Sunteți pe pagina 1din 6

Lucrarea nr.

12
Tehnica dispersiei

1. Scopul lucrării – îl reprezintă prezentarea principiului tehnicii dispersiei precum şi


evidenţierii eficacităţii utilizării acestei tehnici în procesul de căutare.
2. Aspecte teoretice
2.1. Principiul tehnicii dispersiei
Tehnica dispersiei reprezintă o manieră specifică de organizare a unei mulţimi de chei
(elemente cu o cheie specifică) astfel încât regăsirea unei chei să necesite cât mai puţin efort.
Rezolvarea acestei probleme se reduce la găsirea unei asocieri specifice H a mulţimii cheilor
K cu mulţimea adreselor A, adică :
H:K->A
Tehnica dispersiei se aplică structurii de date tip tablou. Ideea pe care se bazează această
metodă este următoarea: se rezervă un volum constant de memorie pentru aşa numitul “tablou
dispersat”. Tabloul dispersat se implementează cu ajutorul unei structuri de date de tip tablou.
Notând cu p numărul elementelor din tabloul dispersat, indicele l care precizează poziţia unui
anumit nod poate lua valori între 0 şi p-1. De asemenea, se notează cu K mulţimea tuturor
cheilor şi cu k o cheie oarecare. Numărul cheilor va fi mai mare decât dimensiunea tabloului.
În acest caz o funcţie H care defineşte o aplicaţie a lui k pe mulţimea tuturor indicilor L ,
astfel :
l=H(k)
unde k este o cheie din mulţimea tuturor cheilor şi l un indice din mulţimea tuturor
indicilor.
Funcţia H de asociere se numeşte funcţie de dispersie şi ea permite ca, pornind de la o
cheie k dată, să se determine indicele asociat acesteia, l. Din acest motiv, tehnica dispersiei se
mai numeşte şi tehnica transformării cheilor, întrucât cheile se transformă, prin intermediul
funcţiei de dispersie, în indici de tablou.
Evident, funcţia H nu este o funcţie bijectivă deoarece numărul cheilor este mult mai mare
decât numărul indicilor. Practic, există mai multe chei cărora le corespunde acelaşi indice.
Principiul metodei este următorul: pentru introducerea unui nod în structură cu cheia k se
determină mai întâi indicele asociat l=H(k), după care se depune la nodul în tabloul dispersat
în poziţia T(l) , unde T este tabloul de dispersie. Dacă în continuare apare o altă cheie k’ care
are acelaşi indice asociat l, adică l=H(k’)=H(k), atunci s-a ajuns la aşa numita situaţie de
coliziune, care se poate rezolva în mai multe moduri, după cum se va ilustra ulterior.

5
Pentru a regăsi un nod cu o cheie k se procedează similar, adică se calculează indicele
asociat cheii k de căutat şi în tabloul de dispersie la indicele găsit ca asociat cheii k se va
căuta cheia. Dacă cheia se va regăsi căutarea se va termina, dar dacă nodul căutat nu se va
regăsi atunci se ajunge într-o situaţie de coliziune, situaţie ce se va rezolva la fel ca şi la
introducerea de noduri.
Aplicarea în practică a tehnicii dispersiei presupune deci rezolvarea a două probleme şi
anume: definirea funcţiei de dispersie H şi tratarea situaţiei de coliziune.
2.2. Determinarea funcţiei de dispersie. Tratarea situaţiei de coliziune
Funcţia de dispersie trebuie să repartizeze cât mai uniform mulţimea cheilor pe mulţimea
indicilor, deoarece astfel se minimalizează probabilitatea coliziunilor iar pe de altă parte
calculele sunt mult mai simple. De asemenea, funcţia de dispersie trebuie să fie uşor
calculabilă. Una dintre funcţiile de dispersie cel mai des utilizate este următoarea:
H(k) = ord(k) mod p
unde ord(p) reprezintă numărul de ordine (întreg) ataşat cheii k şi care precizează a câta
cheie este cheia k în mulţimea K a cheilor iar p este un număr reprezentând numărul maxim
de indici în tabloul dispersat
(0,p-1). În vederea repartizării cât mai uniforme a cheilor pe indici este recomandabil ca p să
fie un număr prim (dar nu obligatoriu).
In cazul cheilor şiruri de caractere se pot utiliza şi următoarele funcţii specifice:


length ( k )
H (k )  ( ord (k[i ]) mod p
i 1


length( k )
H (k )  ( 2 i ord (k[i]) mod p
i 1

Tratarea situaţiei de coliziune presupune generarea unei noi poziţii pentru noua cheie de
inserat. O metodă în acest sens este aceea de a înlănţui într-o listă toate nodurile ale căror
indici primi sunt identici, metoda numindu-se înlănţuire directă. Elementele acestei liste pot
sau nu să aparţină tabelului iniţial. În caz că nu, memoria necesară se alocă din aşa numita
“zonă de depăşire”. Această metodă, care este deosebit de eficientă, are două dezavantaje:
necesitatea menţinerii unor liste secundare şi prelungirea fiecărui nod cu un spaţiu pentru
pointerul necesar înlănţuirii. In această variantă, algoritmul utilizează un tablou de pointeri ce
indică fiecare spre o listă cu nodurile care sunt asociate aceleiaşi intrări din tabloul dispersat.
Algoritmul este următorul:

6
#include "stdafx.h"
#include<stdio.h>
#include<conio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define N 10
div_t y;
typedef struct NOD
{
int cheie;
struct NOD *urm;
};

int DISPERSIE(int x) //functia de dispersie


{
div_t y;
y = div(x, N);
return y.rem;
}
void TIPLIST(NOD *f) //tiparire tablou dispersat
{
NOD *b;
b = f;
while (b != NULL)
{
cout << "\t" << b->cheie;
b = b->urm;
}
cout << " \n ";
}
int main()
{
NOD *el; /* el = un element */
int x, i;
char c;
NOD *t[N]; /* tabloul de dispersie*/
for (i = 0; i<N; i++) t[i] = NULL;

cout << " Introduceti un element intreg \n";


cin >> x;
while (x != 0)
{
el = new NOD;
el->cheie = x;
i = DISPERSIE(x);
/*pozitia in tablou */
el->urm = t[i];
t[i] = el;
cout << "Mai doriti sa introduceti alte elemente?y/n";

7
c = _getch();
if (c != 'y')
{
x = 0;
}
else
{
cout << " \nIntroduceti un element intreg \n";
cin >> x;
}
}
if (c == 'n')
{
for (i = 0; i<N; i++)
{
cout << "\nPe pozitia" << i << " avem elementele : ";
TIPLIST(t[i]);
}
}
}

O altă metodă, numită adresare deschisă, realizează în cazul unei coliziuni parcurgerea după
o anumită regulă a tabloului dispersat, pînă la găsirea primului loc liber, următorul indice
fiind:
- adresare deschisă: indicele consecutiv din tablou faţă de cel care a provocat coliziunea,
l+1, tabloul considerîndu-se circular. Algoritmul are tendinţa de a îngrămădi însă nodurile
în continuarea celor existente, datorită metodei de parcurgere folosite;
- adresare patratică: indicele l+r (l = indicele care a provocat coliziune; r = o valoare care
se iniţializează pe 1 şi se incrementează cu 2 la fiecare nereuşită). Un dezavantaj minor al
acestei metode este acela că se poate întâmpla să nu se găsească nici un element liber,
ajungându-se la depăşirea tabloului, cu toate că în tabel mai există elemente libere.
Totuşi, probabilitatea ca acest lucru să se întâmple este foarte mică, şi în general metoda
este foarte performantă.

2.3. Problemă rezolvată


Să se realizeze un program cu ajutorul căruia se va face dispersia unei mulţimi de numere
date folosindu-se funcţia de dispersie :
k = (x mod n) + 1.

8
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <stdio.h>
using namespace std;
struct coloana_struct
{
int linie[100];
int nr_linii;
};
coloana_struct coloana[100];
int f, n, o, x;
void afisare_coloana(int c)
{
cout<< "\nColoana"<<c<<" : ";
int f;
if (coloana[c].nr_linii == 0) cout << "Goala";
else
for (f = 1; f <= coloana[c].nr_linii; f++)
cout<<coloana[c].linie[f];

}
void afis_coloana()
{
int c;
do
{
cout<<"Dati coloana pe care doriti s-o afisati [1..."<<n<<"] = ";
cin>>c;
} while ((c<1) || (c>n));
afisare_coloana(c);
_getch();
}
void adauga_element()
{
cout<<"Dati elementul ce doriti sa-l introduceti : ";
cin>>x;
int poz = (x%n) + 1;
cout<<"Elementul "<<x<< " a fost adaugat la coloana "<<poz;
coloana[poz].linie[++coloana[poz].nr_linii] = x;
_getch();
}
int main()
{
for (f = 0; f<100; f++) coloana[f].nr_linii = 0;
cout<<"Dati numarul de coloane de dispersie : ";
cin>>n;
do
{
cout<<"\nMeniu pt dispersie cu "<<n<<" coloane";

9
cout<<"\n1.Adaugare numar";
cout<<"\n2.Afisare o anumita coloana";
cout<<"\n3.Afisare toate coloanele";
cout<<"\n4.Iesire";
cout<<"\n\nAlegeti optiunea : ";
cin>>o;
switch (o)
{
case 1:adauga_element(); break;
case 2:afis_coloana(); break;
case 3: {for (f = 1; f <= n; f++)
afisare_coloana(f);
_getch();
} break;
}
} while (o != 4);
}

3. Probleme propuse

1. Se cere să se construiască o tabelă de dispersie conţinând identificatorii dintr-un text (aflat


într-un fisier), fiecare identificator având asociat şi contorul de apariţii. Textul este format din
propoziţii, separate prin caracterul punct. Cuvintele sunt la rândul lor separate cu caracterul
spaţiu, virgulă sau punct-virgulă.
2. Să se realizeze un program prin care se va face dispersia unor studenţi pe baza primei litere
din nume (adică studenţii ale căror nume încep cu „A” vor fi introduşi pe prima „coloană” a
tabloului dispersat, cei cu „B” pe a doua „coloană”, ş.a.m.d) . Datele specifice unui student
vor fi : nume, grupa, medie. Ca şi interfaţă se va folosi un meniu cu următoarele opţiuni:
Adăugare student, Ştergere student, Afişarea tuturor studenţilor al căror nume care începe cu
o anumită literă”, Afişarea integrală a tabelei de dispersie.
3. Principalul dezavantaj al tehnicii dispersiei este acela că lungimea tabloului este fixă.
Presupunând că există un mecanism de alocare dinamică a memoriei prin care, daca tabloul
de dispersie este plin, se poate genera un alt tablou de dispersie, toate cheile din tabloul iniţial
copiindu-se în cel de-al doilea, după care zona ocupată de tabloul iniţial se elibereaza.
Aceasta tehnica se numeste tehnica redispersiei (rehashing). Să se scrie un program care
implementează această tehnică.

10