[[oktatas:programozás:algoritmusok|< Algoritmusok]]
====== Hash ======
* **Szerző:** Sallai András
* Copyright (c) 2014, Sallai András
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC BY-SA 4.0]]
* Web: https://szit.hu
===== A hasításról =====
A hash jelentése zagyvalék, vagdalék, de elegánsabban úgy szoktuk fordítani
az informatikában, hogy hasítás. Esetünkben természetesen adatok hasításáról
van szó. Az informatikában a hasítást mindig egy függvénnyel végezzük, így
ezen túl hasítófüggvényekről beszélünk.
Az informatika két területén is használjuk a hasítófüggvényeket.
Az egyik biztonság, a másik az adattárolás területe.
Az adattárolás területén már az 1950-es évek elejétől ismerős ez
a fogalom. A biztonság területén csak 1980-as évek végétől használjuk.
{{:oktatas:programozás:algoritmusok:hash.png|}}
Ha általánosítjuk a hasítást, az életben is találhatunk hasonló jelenséget.
Ha például informatikai könyveket keresek egy könyvtárban, akkor egyenest
az informatikai polcokhoz megyek, és nem nézem egyenként végig az összes
polc, összes könyvét, mert tudom hol kell keresni.
A számítógép memóriájában is ehhez hasonlóan szeretnénk adatokat tárolni.
Ha keresünk egy adatot ne kelljen végig menni az összes adaton.
A hasítótáblák hatékonysága O(1). Vagyis az adatok számának növekedése
nem jár több művelettel. Összehasonlítva a láncolt lista O(n), a bináris
keresőfák O(log n) hatékonyságúak.
A hasítófüggvényeknek nagy hasznát vesszük szótárak megvalósításánál, ahol
alapvetően három művelet van, új elem hozzáadása, keresés és törlés.
===== Közvetlen címzés =====
{{:oktatas:programozás:algoritmusok:kozvetlen_cimzes.png|}}
===== Hasítás =====
{{:oktatas:programozás:algoritmusok:has_felepitese.png|}}
A hasító függvények egy értékből egy hasítóértéket állítanak elő.
A hasító érték megmutatja az érték helyét a hasítótáblában.
Legyen például néhány főnév, amit szeretnénk tárolni és amelyben szeretnénk
keresni. Az egyszerűség kedvéért most ezek csak az angol ábécé betűit tartalmazzák.
* hal
* kutya
* ablak
* akta
* atka
* akna
* ajak
* kaja
* alga
* talaj
Számozzuk meg az ábécé betűit:
^ első rész ^^^^^^^^^^^^^
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| a | b | c | d | e | f | g | h | i | j | k | l | m |
^ második rész ^^^^^^^^^^^^^
| 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| n | o | p | q | r | s | t | u | v | w | x | y | z |
Vegyünk egy nagyon nagyon egyszerű hasító függvényt. Egy szó kulcsának
meghatározásához adjuk össze a hozzátartozó számokat.
A **hal** szó kulcsa például:
8 + 1 + 12 = 21
A **kutya** ugyanezt az algoritmust követve:
11 + 21 + 20 + 25 + 1 = 68
Az **ablak** szó kulcsa:
1 + 2 + 12 + 1 + 11 = 25
^ a tömb jelenleg ^^
| 0 | |
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| ... | |
| 20 | |
| 21 | hal |
| ... | |
| 25 | ablak |
| ... | |
| 77 | |
| 78 | kutya |
A hasítófüggvényünk úgy tűnik kielégítően működik, a sok üres hely azonban szembetűnő.
===== Tartomány szűkítése =====
Jó lenne ha szűkíteni tudnánk a kulcsok tartományát. Ezt egy maradékképzéses osztással
megtehetjük. Az összeadás végét osszuk el az elemek számával.
A **hal** szó kulcsa:
8 + 1 + 12 = 21
21 % 10 = 1
A **kutya** szó kulcsa:
11 + 21 + 20 + 25 + 1 = 78
78 % 10 = 8
Az **ablak** szó kulcsa:
1 + 2 + 12 + 1 + 11 = 27
27 % 10 = 7
^ a tömb jelenleg ^^
| 0 | |
| 1 | hal |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | ablak |
| 8 | kutya |
| 9 | |
| 10 | |
Az atka és kaja eredménye viszont megegyezik. Az összeadás eredménye ugyan különböző, de 10-zel osztva mindkét esetben 3-t kapunk:
**kaja**:
11 + 1 + 10 + 1 =23
23 % 10 = 3
**atka**:
1 + 20 + 11 + 1 = 33
33 % 10 = 3
Ezért célszerűbb címterületet 10-ről valamelyik közeli prímszámra változtatni. Legyen 13.
Az osztás újra elvégezve a 13-as értékkel a következő helyeket kapjuk:
^ a tömb jelenleg ^^
| 0 | kutya |
| 1 | ablak |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | atka |
| 8 | hal |
| 9 | |
| 10 | kaja |
| 11 | |
| 12 | |
| 13 | |
===== Sorrend figyelembevétele =====
A következő problémát az anagrammák okozzák.
Vegyük az akta szót. A maradékos osztás mellett ütközést kapunk.
Az ütközés oka, hogy a betűk egyeznek, csak más a sorrendjük.
Az eddigi algoritmusunk nem veszi figyelembe a sorrendet.
**akta**:
1 + 11 + 20 + 1 = 23
23 % 13 = 10
**atka**:
1 + 20 + 11 + 1 = 23
23 % 13 = 10
Mindkettő kulcsa egyezik.
A Java nyelv java.lang.String osztályának hashCode() metódusa 31-gyel
való szorzással oldja meg problémát.
Minden elemhez, csak úgy adjuk a következő értéket, hogy előtte
megszorozzuk 31-gyel.
(((a_0 * 31 + a_1) * 31 + a_2) * 31 + a_3) ... * 31 + a_n
Legyen címtér 43. Ekkor 43-mal kell maradékos osztást végezni.
**hal**:
(8 * 31 + 1) * 31 + 12 = 7731
7731 % 43 = 13
**kutya**:
(((11 * 31 + 21) * 31 + 20) * 31 + 25) * 31 + 1 = 10804338
10804338 % 43 = 29
**ablak**:
((1 * 31 + 12) * 31 + 1) * 31 + 11 = 994677
994677 % 43 = 1
**akta**:
((1 * 31 + 11) * 31 + 20) * 31 + 1 = 40983
40983 % 43 = 4
**atka**:
((1 * 31 + 20) * 31 + 11) * 31 + 1 = 49353
49353 % 43 = 32
^ a tömb jelenleg ^^
| 0 | |
| 1 | ablak |
| 2 | |
| 3 | |
| 4 | akta |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | hal |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| ... | |
| 29 | kutya |
| 32 | atka |
| ... | |
| 43 | |
Ezzel az 5 szóval persze a 11 prímszám is megfelelne, de a többi 5 esetén már ismétlődést tapasztalnánk.
A táblázat kihasználtsága nagyon rossz. A tábla mérete 43 és 10 értéket tárolunk benne.
(10 / 43) * 100 = 23 %
===== Lineáris vizsgálat =====
Megkeressük a következő szabad helyet. A következő szó az **akna**.
Az akna a 9-es számot generálja, mint a hal szó. Az akna szó számára
megkeressük a következő szabad helyet, ami a 10:
^ a tömb jelenleg ^^
| 0 | |
| 1 | |
| 2 | ablak |
| 3 | |
| 4 | |
| 5 | |
| 6 | kutya |
| 7 | atka |
| 8 | akta |
| 9 | hal |
| 10 ^ akna ^
A 9-es index után a 10-es szabad.
A következő szó a **kaja**. A kaja ütközik az atka szóval. Mindkettő a 7-es helyen
lenne. A kaja számára megkeressük a következő szabad helyet. A táblázat végéig
nincs üres hely. A táblázat elejéről kezdem a keresést.
^ a tömb jelenleg ^^
| 0 ^ kaja ^
| 1 | |
| 2 | ablak |
| 3 | |
| 4 | |
| 5 | |
| 6 | kutya |
| 7 | atka |
| 8 | akta |
| 9 | hal |
| 10 | akna |
Az első hely szabad, ezért ott elhelyeztük.
Ez már működik. A hatékonyság a O(1) viszont tart a O(n)-be.
===== Láncolás vagy vödör módszer =====
Ha ütközés van, egyszerűen hozzáfűzöm a már meglévő adathoz az újabbat.
A technikát láncolásnak vagy vödörmódszernek is hívják,
mivel egymás után láncoljuk az értékeket, illetve vödrökbe rakjuk azokat.
^ a tömb jelenleg ^^^
| 0 | bolha |
| 1 | |
| 2 | ablak |
| 3 | |
| 4 | |
| 5 | alga | talaj |
| 6 | kutya |
| 7 | atka | kaja |
| 8 | akta |
| 9 | hal | akna |
| 10 | |