Tento článek není dostatečně
ozdrojován, a může tedy obsahovat informace, které je třeba
ověřit.
Jste-li s popisovaným předmětem seznámeni, pomozte doložit uvedená tvrzení doplněním
referencí na
věrohodné zdroje.
Cormackovo hašování (nebo Cormackovo hashování nebo Cormackovo hešování) je jednou z metod dokonalého hašování. Dnes se používá například pro uspořádávání a vyhledávání položek na externích médiích v některých aplikacích (například slovníky, které nemají databázi slov na disku, ale na CD nosiči). Je založeno na existenci primární hašovací funkce , a celé třídy sekundárních hašovacích funkcí . Funkce musí mít obor hodnot roven velikosti adresáře.
Položky jsou ukládány do primárního souboru (pevné velikosti) způsobem, který bude popsán za pomoci adresáře (také pevné velikosti). Protože je i primární soubor i adresář pevné velikosti, řadí se Cormacovo hašování do tzv. statických metod hašování.
Primární soubor si jde představit jako pole pevné velikosti. Adresář vypadá následujícím způsobem:
Adresář
pozice |
p |
i |
r
|
1
|
|
|
|
2
|
|
|
|
...
|
|
|
|
j
|
|
|
|
...
|
|
|
|
|
Význam položek
p je odkaz do primárního souboru
i značí kolikátá hašovací funkce byla použita a
r označuje počet položek v primárním souboru, na které se odkazuje p
pozice je index položky v adresáři.
|
Příklad 1
Adresář
pozice |
p |
i |
r
|
1
|
1 |
2 |
3
|
2
|
|
|
|
...
|
|
|
|
j
|
|
|
|
...
|
|
|
|
|
Primární soubor
pozice |
hodnota
|
0
|
4
|
1
|
5
|
2
|
6
|
3
|
|
4
|
|
5
|
|
...
|
|
|
Tato situace nám popisuje, že v primárním souboru jsou uloženy 3 (r == 3) položky, všechny v jednom bloku (vede na ně jen jeden pointer p), které jdou od sebe rozlišit hašovací funkcí číslo 2 (i == 2) a první z těchto položek je v primárním souboru na pozici 1 (p == 1).
Jak funguje vkládání
Pokud přidáváme položku s klíčem k, nejprve spočteme . Pokud je Adresář[].r == 0, provedeme následující akce
- Adresář[].r = 1
- Adresář[].i = (Pozor, ne 0)!
- V primárním souboru na první volnou pozici s adresou pnew umístíme ukládanou položku
- Adresář[].p = pnew
Pokud je Adresář[].r != 0, provedeme následující akce
- Adresář[].r = Adresář[].r +1
- Najdeme nejmenší i takové, že je různé pro nově vkládanou položku a pro všechny položky v datovém bloku příslušném pointeru Adresář[].p
- Adresář[].i = i
- V primárním souboru na první volnou a dostatečně velkou pozici s adresou pnew umístíme ukládanou položku a všechny položky z bloku Adresář[].p v pořadí, které určí klíč sekundární hašovací funkce
- Adresář[].p = pnew
Příklad 2
Zvolme velikost adresáře M=7.
Potom navrhneme ve tvaru
;
, pro případ r je mocninou 2;
, jinak.
( značí bitový posun vlevo o i bitů)
Postupně budeme přidávat do prázdného souboru položky 6, 3, 13.
, přidání je tedy triviální a struktury mají po přidání prvních dvou tento tvar.
Adresář
pozice |
p |
i |
r
|
0
|
|
|
|
1
|
|
|
|
2
|
|
|
|
3
|
1 |
|
1
|
4
|
|
|
|
5
|
|
|
|
6
|
0 |
|
1
|
|
Primární soubor
pozice |
hodnota
|
0
|
6
|
1
|
3
|
2
|
|
3
|
|
4
|
|
5
|
|
...
|
|
|
Potom se pokusíme přidat 13. , což už je obsazeno.
Zaktualizujeme Adresář[6].r na 2, a najdeme čím je Primární soubor na pozici Adresář[6].p obsazen - (položkou s klíčem 6), a hledáme první sekundární funkci, která tyto klíče rozliší. To je , protože , a .
Takže po vložení 13 budou struktury vypadat následujícím způsobem:
Adresář
pozice |
p |
i |
r
|
0
|
|
|
|
1
|
|
|
|
2
|
|
|
|
3
|
1 |
|
1
|
4
|
|
|
|
5
|
|
|
|
6
|
2 |
0 |
2
|
|
Primární soubor
pozice |
hodnota
|
0
|
|
1
|
3
|
2
|
6
|
3
|
13
|
4
|
|
5
|
|
...
|
|
|
Jak funguje vyhledávání
- spočteme .
- podle Adresář[].r se buď podíváme na přímo příslušné místo do primárního souboru, nebo spočteme příslušnou (Adresář[].i) sekundární hašovací funkci, najdeme příslušný blok a v něm příslušnou položku.
Další často používanou sekundární funkcí je funkce
Předpokládá se, že .
Pseudokód
Zadefinujeme si ještě nějaké datové položky:
typedef struct {int p; int i; int r;} head_1;
typedef struct {int k; int v;} body_1;
head_1 *head = new head_1[s];
body_1 *body = new body_1[];
Vyhledávání
int h(int k, int s) {}
int hi(int i, int k, int r) {}
bool find(int k, int *v) {
j = h(k, s);
if (head[j].r == 0) return false;
else {
body_1 *p = body[head[j].p + hi(head[j].i, k, head[j].r)];
if (p->k != k) return false;
else {*v = p->v; return true;}
}
}
Vkládání
Je trošku složitější, C-like algoritmus není kompletní, ale je názorný:
int free(int size) { /* najde volné místo v primárním souboru s velikostí size */ }
bool insert(body_1 d) {
j=h(d.k, s);
if (head[j].r==0) { // pro daný klíč ještě nemáme alokovaný blok
int p=free(1);
body[p].k=d;
head[j].p=&body[p]; head[j].i=0; head[j].r=1;
} else {
// Jestli už je hodnota d.k v množine head[j].p - head[j].p+head[j].r, vrať false
// Najdi volné místo pro (head[j].r+1) prvků
// Najdi i takové, aby hašovací funkce hi vrátila pro každý z prvků (původní blok+d) různou adresu
// Přemísti prvky ze starého umístění na nové, zapiš nový prvek
// Oprav adresář
}
}