1 / 24

Miskolci Egyetem Informatikai Intézet Általános Informatikai Tanszé k Pance Miklós

Miskolci Egyetem Informatikai Intézet Általános Informatikai Tanszé k Pance Miklós Adatstruktúrák, algoritmusok előadásvázlat Miskolc, 2004 Technikai közreműködő: Imre Mihály, műszaki informatikus hallgató. Tömörítés: LZ 77. LZ 77 Sliding Window compression :

cairo-woods
Download Presentation

Miskolci Egyetem Informatikai Intézet Általános Informatikai Tanszé k Pance Miklós

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Miskolci Egyetem Informatikai Intézet Általános Informatikai Tanszék Pance Miklós Adatstruktúrák, algoritmusok előadásvázlat Miskolc, 2004 Technikai közreműködő: Imre Mihály, műszaki informatikus hallgató

  2. Tömörítés: LZ 77 LZ 77 Sliding Window compression: Alapja: Jacob Ziv, Abraham Lampel: „A Universal Algorithm for Sequental Data Compression” IEEE Transactions on Information Theory. Az LZ 77 tömörítő szótárként az előzőleg látott szöveget használja. Az input szöveg kifejezéseit a szótárra mutató pointerekkel helyettesíti. A tömörítés foka függ a szótár kifejezések hosszától, az előzőleg látott szövegre nyíló ablak nagyságától, és a forrás szövegnek a modellre vonatkozó entrópiájától. A szöveg ablak két részre osztott. Az első a jelenleg dekódolt szöveg nagy blokkja, a második általábansokkal kisebb előrenéző buffer. Az előrenéző bufferben az inputszövegáramból olvasott karakterek vannak, amit még nem kódoltunk be. 2

  3. for(i = 0; i <MAX–1; i++) \r for(j =i+1;j <MAX ;j++) \r szöveg ablak előrenéző puffer Tömörítés: LZ77 A szöveg ablak szokásos mérete általában néhány ezer karakter. Az előrenéző buffer általában sokkal kisebb, tíz – száz karakter. • A szöveg ablak 64 karakter, ebből 16-ot használ az előrenéző puffer. Az LZ 77 eredetileg token sorozatot ad ki, melyek mindegyike három adatot tartalmaz az aktuális előrenéző puffer, változó hosszúságú kifejezésére: • mutató egy szöveg ablakbeli kifejezésre, • a kifejezés hossza, • a kifejezést követő első karakter az előrenéző pufferben. • A példában az előrenéző puffer tartalma: ’< MAX; j++)\r’ . A puffert kutatva, megtaláljuk ’<MAX’ kifejezést a szöveg ablak 14. pozícióján és 4 karakter egyezik meg. Az előrenéző puffer első nem található karaktere a <space> . Így a token: 14, 4, <space> 3

  4. = 0; i <MAX –1; i++) \r for(j =i+1;j<MAX ;j++) \r a[i] szöveg ablak előrenéző puffer Tömörítés: LZ 77 Ezután a tömörítő program a szöveg ablakot 5 karakterrel eltolja, ami az éppen elkódolt (encode) kifejezés szélessége. Ezután 5 új jelet olvas az előrenéző pufferbe és az eljárás ismétlődik. Ezután a ’;j+’ kifejezést kódolja be : 40, 2, ’+’ Ha nincs megfelelés, akkor 0 hosszúságú kifejezést ad ki, pl.: 0, 0, ’*’ . Ez nem hatékony, de így bármilyen szöveg bekódolható. Egy durva implementáció (brute force): megkeresi a leghosszabb egyezést, bekódol, eltol. 4

  5. Tömörítés: LZ 77 Betömörítés: int window_cmp(char *w, int i, int j, int length) { int count = 0; while(length--) { if(w[i++] == w[j++]) count++; else return(count); } return(count); } 5

  6. Tömörítés: LZ 77 match_poz = 0; match_len = 0; for(i = 0; i < ( Winsize - elonezsize ); i++) { len = window_cmp( win, i, elonez, elonezsize); if(len > match_len) { match_poz = i; match_len = len; } } encode(match_poz, match_len, win[ elonez + match_len]); memmove(win, win + match_len +1, Winsize - match_len); for(i = 0; i < match_len + 1; i++) win [ Winsize - match_len + i] = getc(input); 6

  7. Tömörítés: LZ 77 Kitömörítés (decompression): Nincs összehasonlítás. Beolvassa a tokent, kiírja a kifejezést, kiírja a követő karaktert, eltol, ismétel végig. decode(&match_poz, &match_len; &charac); fwrite(win+ match_poz, 1, match_len, output); putc(charac, output); for(i = 0; i < match_len; i++) win[elonez + i] = win[match_poz + i]; win[elonez + i] = charac; memmove(win, win + match_len + 1, Winsize - match_len); 7

  8. - - - - - - - - - - - - - - - - - - - A - - - - - - - - - - - - - - - - - - - A A A A A A A A A A A match_poz elonez_puff Tömörítés: LZ 77 Ennek a kitömörítő eljárásnak egy érdekes mellékhatása, hogy használhat olyan kifejezést is egy létező kifejezés bekódolására, amit még nem enkódolt. Pl. egy fájl, ami 100 A betűt tartalmaz egymásután: Az első A enkódja: (0,0,’A’) Ezután a következő 9 A betű kódolhatóígy is: (38,9,’A’) . Bár mi láthatjuk a kifejezést az előrenéző pufferben (az A karaktereket), de a dekóder erre nem képes. Amikor a dekóder megkapja a (38,9,’A’) tokent, akkor a puffere: 8

  9. A A A A A A A A A A A A A match_poz +i match_poz +i elonez_puff + i elonez_puff + i Tömörítés: LZ 77 De a decompress algoritmus ezt meg tudja oldani: a ciklusban a match_poz –ból másol az elonez pufferbe: végül Ez az LZ 77 tömörítés gyors alkalmazkodását bizonyítja. Bekódolt 10 karakteres sorozatot, amikor a szótárábanmég csak egyetlen karakter volt belőle. 9

  10. Tömörítés: LZ77 Problémák az LZ 77-tel A fenti implementáció az algoritmusnak egy laza interpretációja. Nyilvánvaló a teljesítmény szűk keresztmetszete (bottleneck), a string összehasonlítás: a szöveg ablak minden pozícióján összevet az előrenéző pufferrel. Ez még csak romlik, ha a teljesítmény (tömörítés foka) fokozására növeljük az ablak méretét, azaz a szótár méretét. A dekódolást ez nem befolyásolja. A másik probléma a csúszó ablak kezelésének módja, kényelmességből itt a csúszó ablakot úgy kezeltük mintha ez valóban végig csúszna a szövegen. Helyette a kezdő és vég pointereket csúsztatjuk a puffer (a teljes szöveg) mentén. 10

  11. Tömörítés: LZ77 De ekkor a modulo indexet kell használnunk: int window_cmp(char* w, int i, int j, int len) { count = 0; while(len--) { if (w[i] == w[j]) count++; else return(count); i = ++i % winsize; j = ++j % winsize; } return(count); } 11

  12. Tömörítés: LZ 77 Egy enkód probléma Ha nem talál egyező kifejezést, akkor az egyetlen karakter bekódolására is a három komponensű tokent használja. Pl. használjunk egy 4096 bájtos ablakot, 16 bájtos előrenéző pufferrel. Ehhez 20 bit az ablak pozíció, 4 bit a kifejezés hossz = 24, egyetlen 8 bites jel bekódolására. 12

  13. Tömörítés: LZSS 1. változtatás: a kifejezés tárolása Az LZ 77-ben a kifejezések folytonos szövegblokként tárolódnak, minden szervezettség nélkül. Az LZ SS bináris kereső fa szerkezetet használ a kifejezések tárolására. Így a leghosszabb megegyező kifejezés megtalálása a korábbi winsize* kifejezésméret helyett annak logaritmusával arányos. Ez bátoríthat a nagyobb ablakokkal való kísérletezésre. Pl. az ablak megduplázása az összehasonlítási időt csak 1 egységgel növeli, míg az LZ 77-nél ez duplája. 2. változtatás: a token kialakítása LZ 77 : a token 3 részből áll LZ SS megengedi a pointerek és karakterek szabad keveredését. A beindulásnál csupa ismeretlen kifejezés jön ... Az LZ SS a tokenek elé egybites jelzőt tesz az offset/hossz páros illetve az egyetlen karakter jelzésére az outputban. Ennél kisebb gondot okoz, hogy a követő karaktert is kiírja. 13

  14. Tömörítés: LZ • Az alkalmazott adatszerkezetek: • unsigned char win[winsize];nem az ablak csúszik, hanem a pointerek, ekkor az (i+1) mod winsize művelet hatékonyabban végezhető, ha a winsize 2 hatványa • a kifejezések tárolására bináris kereső fát használunk: • struct{ • int parent; • int smaller_child; • int larger_child; • } tree[winsize + 1]; 14

  15. Tömörítés: LZ A tree[Winsize] elem a fa gyökerét jelöli ki, ehhez nem tartozik kifejezés, nincs kisebb, nagyobb gyereke, a nagyobb gyerek indexe magára a fa gyökerére mutat. Ez csökkenti a feldolgozási időt és egyszerűsíti a kódot. Pl. törlésnél ilyen kódrészlet: tree[tree[i].parent].child = tree[i].child mivel a gyökérre mutató pointert ugyanabban a fában tároljuk, ezért nem kell külön ellenőrzés, arra, hogy az a gyökér-e. Mégha i a gyökér csomópont, akkor is a tree[i].parent még érvényes csomópontra mutat a fában. Egy további szokatlan jellemző, hogy az LZ SS egy speciális kódot használ a tömörített adat vége elérésének jelzésére. Ebben az esetben a zérus ablak index az adatáram végét jelzi. Így ez nem használható érvényes kifejezésként. 15

  16. Tömörítés: LZ A 0 kifejezést nem használjuk, így a 0 csomópontot speciális UNUSED indexként használva kódot takaríthatunk meg. Pl. a törlés kódrészleténél: if (tree[i].smaller_child != UNUSED) tree[tree[i].smaller_child].parent = tree[i].parent; if (tree[i].larger_child != UNUSED) tree[tree[i].larger_child].parent = tree[i].parent; De ha az UNUSED index egy megengedett tárolóterületre mutat, akkor az érvényességi vizsgálat elhagyható. tree[tree[i].smaller_child].parent = tree[i].parent; tree[tree[i].larger_child].parent = tree[i].parent; Mivel a tree[0] értéket sohasem használjuk navigálásra, hibát nem okoz, és jelentős CPU időt takarít meg. 16

  17. Tömörítés: LZ Kiegyensúlyozás A kereső fa könnyen láncolt listává alakulhat, mivel a fájlokban gyakran előfordulnak csökkenő vagy növekvő kifejezések. Ezek gyakran megesnek, de a csúszó ablak természete folytán gyorsan ki is mennek a fából. Ezért fa kiegyensúlyozást általában nem építenek be. „Greedy” vagy a „lehető legjobb” Az LZ 77 és az LZ SS is greedy algoritmus, mivel nem néznek előre az input áramba, hogy azt analizálják az indexek és karakterek legjobb kombinációja érdekében. A gyakorlatban néhány % megtakarítás mutatkozik, a feldolgozási idő pedig jelentősen nő. Néhány jó heurisztikát szoktak mindössze használni és ez a greedy algoritmus határozottan jó. 17

  18. Tömörítés: LZ • Javítások: • Előre feltölteni az ablakot Winsize-elonezsize karakterrel és utána adjuk a megfelelő stringeket a bináris fához. De mivel töltsük fel előre? • Lehet kísérletezni az index és a kódhossz bitjeinek növelésével. • „ghost buffer” a szöveg ablak végére, ami az ablak első 17 karakterét tartalmazza (16 a mérete az elorenez ablaknak) így a modulo aritmetika kihagyható, de ezt karban kell tartani. • Blokkolt I/O. • String duplikátumok kezelése:a fába nem tesszük, de az ablakban benne van. 18

  19. Tömörítés: LZ78 • jelsorozat, szótárat használ, • ez a szótár a tömörítés végéig él (nem kerülnek ki belőle elemek), fokozatosan bővül (tanul) az új jelsorozatokkal, • ha megtelik nem vesz fel újat, • induláshoz a szótárnak 1 eleme van: üres string. • A szótár felépítése: jelsorozat, kód. 19

  20. Tömörítés: LZ78 • A tömörítés elve: • adott pozíción vagyunk • megkeressük a szövegben azt a leghosszabb jelsorozatot, ami már benne van a szótárban (kezdetben csak rövidebbek, később hosszabbak), • a tömörített állományba a kódot írjuk ki, • megtalált rész + az őt követő karakter együtt mint egy új jelsorozat bekerül a szótárba új kódértékkel, • a tömörített fájlba kiírja a követő karaktert is, így nem kell a szótárat is hozzáírni, hanem az felépíthető a dekódolás során. 20

  21. jelsor jelsor " " W W A A WA WA T T O O S S OW OW kód kód 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 Tömörítés: LZ78 Példa: WAWATOSOWA Tömörítve: 0W | 0A | 1A | 0T | 0O | 0S | 5W | 2- Vissza: WAWATOSOWA 21

  22. Tömörítés: LZW • LZW (1984 Terry Welsh) •  Az LZ 78 javított változata, induláskor az összes jellel feltöltjük a szótárt. • A kódolás menete: • megkeressük a szótárban is meglévő leghosszabb részt és kiírjuk a kódját, • ez a rész is a követő karakter új jelsorozatként kerül a szótárba, • a követő karakteren indulva indulva folytatjuk a vizsgálatot. 22

  23. " WA WA AW WAT TO WW OS WWW SO 256 0 255 256 257 258 259 256 260 257 261 Tömörítés: LZW WAWATOSO W | A | 256 | T | O | S | O WAWATOSO WWWWWW W | 256 | 257 23

  24. Tömörítés Forrás: Mark Nelson: The Data Compression Book M&T, 1991. ISBN 1-55851-216-0 24

More Related