Bloom-szűrés

Általános tudnivalók

Használható segédanyagok: GHC dokumentáció, Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármi kérdés, észrevétel felmerül azt a felügyelőnek jelezzétek, NE a mellettetek ülőnek!

A feladat összefoglaló leírása

Ebben a feladatban az ún. Bloom-szűrő egyszerűsített változatát, és annak egy alkalmazását kell megvalósítani. A Bloom-szűrő egy valószínűségi adatszerkezet, amelyet annak eldöntésére használnak, hogy egy elem elem-e egy adott halmaznak. Működéséből fakadóan a szűrő tévesen adhat pozitív választ (,,az elem benne van’’), de abban (bizonyíthatóan) sosem téved, hogy egy elem nincs benne a halmazban. Azonban minél több elemet adunk hozzá a halmazhoz, annál nagyobb a valószínűsége a téves válaszoknak. Elemeket csupán hozzáadni tudunk, elvenni nem.

Segítségül a következő típusokat fogjuk majd használni:

data BitVector = BV [Int]
  deriving (Show,Eq,Data,Typeable)
data BloomFilter = BLF BitVector
  deriving (Show,Eq,Data,Typeable)

Additív hasítófüggvény (1 pont)

A Bloom-szűrő működésének definiálásához elsőként szükségünk lesz néhány hasítófüggvényre. Ezeknek az a feladata, hogy byte-ok (0 és 255 közti értékek) sorozatából készítsenek egy azonosítót. Ezért ezeket gyakran kulcstranszformációs függvények is nevezik. Előnyük, hogy az értékhez tartozó kulcs közvetlenül az értékből kiszámítható.

Az első ilyen az additív hasítófüggvény. Működése a következő: adjuk össze a sorozat elemeit és az összegből képezzünk maradékot egy előre megadott prímszámmal.

additiveHash :: Int -> [Int] -> Int

Test>
55 :: Int
Test>
78 :: Int

Pearson-féle hasítófüggvény (1 pont)

A másik ilyen függvényünket a Pearson-féle algoritmus szerint számítjuk ki.

Működése:

Segítség: Az xor művelethez használjuk a Data.Bits.xor függvényt!

pearsonHash :: [Int] -> [Int] -> Int

Test>
11 :: Int
Test>
189 :: Int

Hasítóértékek meghatározása egy listához (2 pont)

A hasítófüggvények definiálása után készítenünk kell egy olyan függvényt, amely egy adott sorozathoz előállítja a függvények által nyert értékeket (egy listában).

Elsőként az additív hasítófüggvényt alkalmazzuk, melynek paramétere 191, másodikként a Pearson-féle hasítófüggvény alkalmazzuk a [255,254..0] paraméterrel.

hashes :: [Int] -> [Int]

Test>
[84, 100] :: [Int]
Test>
[118, 189] :: [Int]

Üres bitvektor (1 pont)

Ezt követően meg kell valósítanunk a bitvektorokat. Ezeket egy lista segítségével úgy ábrázoljuk, hogy csak azon elemek indexeit tároljuk, amelyek értéke 1. (Ebből pedig következik implicite, hogy a többi értéke 0).

Ennek első lépéseként az üres vektort kell ebben a formában kifejeznünk.

empty :: BitVector

Egy bit hozzáadása a bitvektorhoz (2 pont)

Készítsük el azt a függvényt, amellyel egy bitvektor adott indexű bitjét tudjuk beállítani az 1 értékre!

add :: BitVector -> Int -> BitVector

Test>
BV [2] :: BitVector
Test>
BV [4, 2] :: BitVector

Konverzió Bloom-szűrők és bitvektorok között (1 pont)

Készítsük el azt a függvényt, amellyel egy bitvektorból Bloom-szűrőt tudunk létrehozni!

toBloom :: BitVector -> BloomFilter

Test>
BLF (BV [10]) :: BloomFilter

A Bloom-szűrő bővítése (2 pont)

Az eddigi függvények segítségével most már le tudjuk írni a Bloom-szűrő bővítését. Ez úgy működik, hogy vesszük a bővítendő elemet mint értékek sorozatát, erre kiszámítjuk az összes hasítófüggvényünk értékét, majd a nekik megfelelő indexű biteket a szűrőt ábrázoló vektorban egyesre állítjuk.

bloomAdd :: BloomFilter -> [Int] -> BloomFilter

Test>
BLF (BV [55, 11]) :: BloomFilter
Test>
BLF (BV [155, 31, 55, 11]) :: BloomFilter

Elemvizsgálat bitvektorokra (1 pont)

A szűréshez definiáljuk azt a függvényt, amellyel egy bitvektorról meg tudjuk mondani, hogy az adott indexű bitje 1 értékű-e!

has :: BitVector -> Int -> Bool

Test>
True :: Bool
Test>
False :: Bool

A Bloom-féle szűrés (2 pont)

Írjuk meg a Bloom-szűrést! Működése: vegyük a vizsgálandó paramétert mint értékek sorozatát, számítsuk ki rá az összes hasítófüggvényünk értékét, majd ellenőrizzük, hogy az így megadott pozíciók mindegyikén 1 értékek állnak-e! Amennyiben igen, akkor tekintsük úgy, hogy elemet elfogadja a szűrő, ellenkező esetben pedig nem.

bloomQuery :: BloomFilter -> [Int] -> Bool

Test>
True :: Bool
Test>
False :: Bool

Karakterlánc normalizálása (1 pont)

A Bloom-szűrőket gyakran alkalmazzák helyesírás-ellenőrzőkben, ahol nagy mennyiségű adat közt kell keresni gyorsan, például amikor a begépelt szavakról döntjük el, hogy helyesek-e (szerepelnek-e a helyesnek nyilvántartott szavak közt).

Ehhez elsőként készítsünk egy olyan függvényt, amely egy szót normalizál: csak az ábécé betűit tartja meg belőle, és ezeket előbb nagybetűkké, majd karakterkódokká alakítja azokat!

normalize :: String -> [Int]

Test>
[74, 85, 75, 65, 83] :: [Int]
Test>
[79, 75, 84, 79, 66, 69, 82] :: [Int]

Egy Bloom-szűrő felépítése szótárral (1 pont)

Ezen kívül még az ellenőrzéshez fel kell építenünk a szűrőt. Ez gyakorlatilag azt jelenti, hogy vesszük a helyesnek definiált szavak egy listáját, normalizáljuk, és egy kezdetben üres Bloom-szűrőt folyamatosan bővítünk.

wordFilter :: [String] -> BloomFilter

Test>
False :: Bool
Test>
True :: Bool

Hibás szavak kiszűrése a szövegből (2 pont)

Végül adjuk meg az ellenőrző függvényt, amely egy korábban már létrehozott szűrő segítségével megadja azon szavak sorszámát a szövegben, amelyek nem helyesek (nem fogadhatóak el a szűrő szerint)!

spellCheck :: BloomFilter -> String -> [Int]

Test>
[] :: [Int]
Test>
[0] :: [Int]

Pontozás