Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Algorytmy i struktury danych
Tablice haszowane
2
Klucz Klucz – „wyróżnik danych” np.
pesel, nr dowodu, nr indeksu data ur.+imię +nazwisko,, Założymy, że klucze to liczby, zawsze można zbudować funkcję transformującą np. napis na liczbę: B. proste (i niespecjalnie dobre) rozwiązanie: można np. zsumować kody odpowiadające literom „ALA” ->
3
Tablica z adresowaniem bezpośrednim
! Małe uniwersum kluczy ! 1 2 Klucz Dane 3 Klucz Dane 4 2 3 5 Klucz Dane K 6 5 7 8 8 Klucz Dane 9
4
Adresowanie bezpośrednie - implementacja
def DirectAdress_Search(table, key): return table[key] def DirectAdress_Insert(table, data): table[data.key] = data def DirectAdress_Delete(table, key): table[key] = None w Pythonie [] nie jest tablicą
5
Tablica z funkcją haszującą
Funkcja haszująca odwzorowuje klucz w przestrzeń adresową tablicy 1 2 Klucz Dane 3 Klucz Dane Uniwersum wszystkich kluczy 4 k2 k3 5 Klucz Dane K 6 k5 7 k8 8 Klucz Dane 9
6
Funkcja haszująca F. haszująca - odwzorowuje klucz w przestrzeń adresową (zwykle mniejszą niż uniwersum), tj. dziedzinę poprawnych adresów tablicy h(k) powinno należeć do przestrzeni adresowej tablicy dla dowolnego (legalnego) klucza Wymagania: generowanie adresów dla rzeczywistego zbioru rekordów w sposób jak najbardziej równomierny (rozproszony) łatwość obliczenia Wskazane jest sprawdzenie wybranej f. haszująca na fragmencie rzeczywistych danych
7
Haszowanie modularne h(k) = k mod m, gdzie m jest rozmiarem tablicy
Dobre m nie powinno być równe 2p, 10p – gdyż takie haszowanie ignoruje bardzo znaczące bity (cyfry) Dobre m nie powinno być równe 2p-1, 10p-1 gdyż daje identyczne wartości na ciągach, dla których przestawiono bity (cyfry) Niezłe są np. liczby pierwsze niezbyt bliskie potęgom 2
8
Haszowanie przez mnożenie
h(k) = m (kA mod 1), gdzie kA mod 1 = kA - kA A zwykle jest liczbą z przedziału 0..1 m jest zwykle potęgą 2 lub 10, gdyż wtedy h(k) = pewna ilość cyfr po przecinku z kA W praktyce bardzo dobre A to np. (sqrt(5)-1)/2 =
9
Haszowanie przez randomizację
h(k) = Rand (k) Metoda kwadratu środka: wydziel z klucza pewną jego część (np. środek), potraktuj jako liczbę binarną, po czym podnieś ją do kwadratu. Metoda składania: podziel klucz na części (segmenty), potraktuj je jako liczby binarne, po czym dodaj je do siebie arytmetycznie. Metoda sumy modulo 2: podziel klucz na części (segmenty), potraktuj je jako ciągi bitów i dodaj je do siebie modulo 2. W praktyce dla uzyskania dobrej losowości wymagane sa duże wartości stad często stosuje się ta metode w połączeniu z h. modularnym
10
Haszowanie uniwersalne
H – rodzina funkcji haszujących Na początku pracy losowo wybieramy jedną funkcję haszującą z całej rodziny i stosujemy ją od tej pory hH Brak możliwości „złośliwego” doboru elementów
11
Rozwiązywanie kolizji przez łańcuchowanie oddzielne
Listy zawierają elementy, dla których h(k) jest takie samo-synon. Kolejność elementów na liście jest przypadkowa 1 2 k2 Dane k12 Dane 3 k3 Dane 4 k12 k2 k3 5 k15 Dane k5 Dane 6 k15 k5 7 k8 8 k8 Dane h(k) = k mod 10 9
12
Łańcuchowanie oddzielne – impl.
def ChainHash_Search(table, key): return FindNode(table[h(key)], key) def ChainHash_Insert(table, data): pos = h(data.key) table[pos] = AddFirst(table[pos], data) def ChainHash_Delete(table, key): pos = h(x.key) table[pos] = RemoveNode(table[pos], key)
13
Łańcuchowanie oddzielne - właściwości
- współczynnik zapełnienia = ilość elementów / rozmiar tablicy Pesymistyczny czas wyszukiwania = (n) Jeżeli h rozmieszcza klucze równomiernie, to dla Oczekiwany czas wyszukiwania = (1+ )
14
Adresowanie otwarte Dla rozwiązywania konfliktów nie stosuje się list
Wszystkie elementy (wskaźniki) zapisywane są bezpośrednio w tablicy h(k, i) = ( h’(k) + g(i) ) gdzie: k – klucz, i – numer próby (0..N-1), N – rozmiar tablicy Aby umieścić element w tablicy sprawdzamy h(k, 0) jeśli zajęte, to h(k, 1) itd aż do h(k, N-1).
15
Adresowanie otwarte przykład kolizji
1 Kolejność dodawania: k12, k2, k4, k3 2 k12 Dane 3 k2 Dane 4 k4 Dane k12 k2 k3 5 k3 Dane 6 k4 7 8 h(k) = (k+i) mod 10 9
16
Adresowanie otwarte – implem.
def Hash_Insert(table, data): for i in range(0, MAX): j = h(x.key, i) if table[j] == None: table[j] = data return ERROR „przepełnienie tablicy”
17
Adresowanie otwarte – implem.
def Hash_Search(table, key): for in range(0, MAX): j = h(key, i) if table[j] == None: return None if table[j].key == key: return table[j]
18
Adresowanie otwarte – usuwanie elementów
Problem: Nie można wpisywać None Rozwiązanie: Nowa stała np.: DELETED Należy wtedy zmodyfikować przeszukiwanie if table[j] == None: return None if table[j] == DELETED: continue if table[j].key == key: return table[j] i dodawanie if table[j] == None or table[j] == DELETED:
19
Rodzaje adresowania otwartego
Liniowe: h(k,i) = (h’(k) + i ) mod m Wadą jest grupowanie się elementów Kwadratowe: h(k,i) = (h’(k) + c1i + c2i2) mod m Wadą jest (mniej groźne) grupowanie wtórne Sześcienne: h(k,i) = (h’(k) + c1i + c2i2 + c3i3) mod m Dwukrotne: h(k,i) = (h1(k) + h2(i)) mod m Grupowanie wtórne jest znikome
20
Metoda otwarta - właściwości
- współczynnik zapełnienia = ilość elementów / rozmiar tablicy Pesymistyczny czas wyszukiwania = (n) Dla liniowej funkcji g oczekiwany czas wyszukiwania z sukcesem 1+1/(1- ) z porażką to 1+1/(1- )2 Dla pseudolosowej funkcji g oczekiwany czas wyszuk. z sukcesem -1 (1+ln (1/(1- ))) z porażką to 1/(1- )
21
Uniwersum kluczy Powinno być nie mniejsze niż rozmiar tablicy
Sumowanie kodów liter np. „ALA” -> Stosunkowo mały rozmiar uniwersum, np. dla 30 znaków 30*256 = 7680 Sumowanie par (lub dłuższych ciągów liter), co daje większą przestrzeń adresów „ALA” -> 65* *256 W uniwersum są „dziury” bo np. znaki mają kody >=32 i zwykle mniejsze niż 127 Suma iloczynów kodów par „ALA” -> 65* Nierównomierny rozkład wartości, np. więcej l. parzystych
22
Przykład Tablica 106 elementów adresowana nazwiskiem i imieniem Znaki:
W ogólności jeden znak dla ASCII znaki niedrukowalne, 32-47, 91-96, znaczki różne, cyfry, 65-90, – litery, znaki graficzne (polskie litery) UNICODE – jeden znak 0 – (216-1), dużo bardzo rzadko używanych znaków. Przy obliczeniach można pominąć znaki różne od liter (ewentualnie cyfr) Polskie znaki diakrytyczne: można zamienić na ich łacińskie odpowiedniki lub pominąć. Zamieniamy znaki na małe. Stąd jeden znak może mieć kod od 97 do 122. log = log = Czyli maksymalna waga dla znaku powinna wynosić co najmniej 265.
23
Przykład DIGITGROPUP = 5 BASE = ord("Z")-ord("A")+1 def Key(key): digitPos = 0 keyVal = 0 for c in key: c = ConvertChar(c) # konwersja na duze litery + # ew zamiana polskich znakow if c>="A" and c<="Z": digit = ord(c)-ord("A") keyVal = keyVal + digit * pow(BASE, digitPos) digitPos = (digitPos+1) % (DIGITGROPUP+1) return keyVal
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.