IZPELJANKE ALGORITMA LZW - COnnecting REpositories · 2017. 11. 28. · Izpeljanke algoritma LZW...

38
Jure Sreš IZPELJANKE ALGORITMA LZW Diplomsko delo Maribor, september 2016

Transcript of IZPELJANKE ALGORITMA LZW - COnnecting REpositories · 2017. 11. 28. · Izpeljanke algoritma LZW...

  • Jure Sreš

    IZPELJANKE ALGORITMA LZW

    Diplomsko delo

    Maribor, september 2016

  • IZPELJANKE ALGORITMA LZW

    Diplomsko delo

    Študent(ka): Jure Sreš

    Študijski program: Računalništvo in informacijske tehnologije (UN)

    Smer: /

    Mentor: doc. dr. Bogdan Lipuš

  • i

  • ii

    Zahvala

    Zahvaljujem se vsem, ki so kakorkoli pripomogli k

    nastanku tega dela.

  • Izpeljanke algoritma LZW

    Ključne besede: stiskanje podatkov, stiskanje s slovarjem, primerjava algoritmov

    UDK: 004.627(043.2)

    Povzetek

    Algoritmi za stiskanje podatkov so v računalništvu prisotni že od samega začetka. Igrajo

    pomembno vlogo pri prenosu in shranjevanju velikih količin podatkov, razdelimo pa jih

    lahko na več načinov. Poznamo izgubno in brezizgubno stiskanje. Glede na način

    izvajanja ločimo statistično in stiskanje s slovarjem. V tem diplomskem delu bomo

    predstavili algoritme LZW, LZAP, LZMW in LZY, ki uporabljajo slovar in podatke stisnejo

    brezizgubno. Predstavili bomo njihovo delovanje in primerjali rezultate.

    iii

  • Variants of the LZW algorithm

    Keywords: data compression, dictionary-based compression, algorithm comparison

    UDK: 004.627(043.2)

    Abstract

    Data compression algorithms are present in the computer science from the very

    beginning. They play a major role in transmitting and storing a large amount of data and

    can be divided in various ways. In general, they can be entropy or dictionary-based. A

    compression can be performed lossy or lossless. We present the algorithms LZW, LZAP,

    LZMW and LZY, that compress data losslessly and use dictionary in the process. We

    examine the presented algorithms and the results of compression.

    iv

  • KAZALO VSEBINE

    1 UVOD.............................................................................................................................1

    2 STISKANJE PODATKOV S SLOVARJEM......................................................................3

    3 ALGORITEM LZW..........................................................................................................5

    3.1 KODIRANJE............................................................................................................5

    3.2 DEKODIRANJE.......................................................................................................7

    3.3 IMPLEMENTACIJA.................................................................................................8

    4 ALGORITEM LZMW.....................................................................................................10

    4.1 KODIRANJE..........................................................................................................10

    4.2 DEKODIRANJE.....................................................................................................12

    4.3 IMPLEMENTACIJA...............................................................................................12

    5 ALGORITEM LZAP.......................................................................................................14

    5.1 KODIRANJE..........................................................................................................14

    5.2 DEKODIRANJE.....................................................................................................15

    5.3 IMPLEMENTACIJA...............................................................................................16

    6 ALGORITEM LZY.........................................................................................................18

    6.1 KODIRANJE..........................................................................................................18

    6.2 DEKODIRANJE.....................................................................................................20

    6.3 IMPLEMENTACIJA...............................................................................................21

    7 REZULTATI...................................................................................................................24

    7.1 LZW......................................................................................................................26

    7.2 LZAP.....................................................................................................................27

    7.3 LZY.......................................................................................................................28

    8 SKLEP..........................................................................................................................29

    v

  • KAZALO PREGLEDNIC

    Preglednica 2.1: Razvrstitev algoritmov..............................................................................4

    Preglednica 3.1: Postopek kodiranja z algoritmom LZW.....................................................6

    Preglednica 3.2: Slovar po koncu kodiranja........................................................................6

    Preglednica 3.3: Postopek dekodiranja z algoritmom LZW.................................................7

    Preglednica 4.1: Postopek kodiranja z algoritmom LZMW................................................11

    Preglednica 4.2: Vnosi v slovar po končanem izvajanju algoritma LZMW.........................11

    Preglednica 4.3: Postopek dekodiranja z algoritmom LZMW............................................12

    Preglednica 5.1: Postopek kodiranja z algoritmom LZAP.................................................15

    Preglednica 5.2: Vnosi v slovar po končanem izvajanju algoritma LZAP..........................15

    Preglednica 5.3: Postopek dekodiranja z algoritmom LZAP.............................................16

    Preglednica 6.1: Postopek kodiranja z algoritmom LZY....................................................19

    Preglednica 6.2: Vnosi v slovar po končanem izvajanju algoritma LZY............................20

    Preglednica 6.3: Postopek dekodiranja z algoritmom LZY................................................21

    Preglednica 7.1: Rezultati algoritma LZW.........................................................................27

    Preglednica 7.2: Rezultati algoritma LZAP.......................................................................28

    Preglednica 7.3: Rezultati algoritma LZY..........................................................................28

    KAZALO SLIK

    Slika 3.1: Psevdokod kodiranja z algoritmom LZW.............................................................8

    Slika 3.2: Psevdokod dekodiranja z algoritmom LZW.........................................................9

    Slika 4.1: Psevdokod kodiranja z algoritmom LZMW........................................................13

    Slika 5.1: Psevdokod kodiranja z algoritmom LZAP..........................................................17

    Slika 6.1: Psevdokod kodiranja z algoritmom LZY............................................................22

    Slika 6.2: Psevdokod dekodiranja z algoritmom LZY........................................................23

    Slika 7.1: Grafikon primerjav faktorjev stiskanja algoritmov..............................................25

    Slika 7.2: Grafikon primerjave porabljenega časa pri kodiranju........................................25

    Slika 7.3: Grafikon primerjave porabljenega časa pri dekodiranju.....................................26

    vi

  • Izpeljanke algoritma LZW

    1 UVOD

    Stiskanje podatkov je proces, katerega cilj je pretvoriti podatke tako, da ti po obdelavi

    zasedejo čim manj prostora, vendar ohranijo informacije. Ideja o stiskanju podatkov je

    prisotna že od nekdaj, najbolj pa je ta problem postal očiten z izumom računalnika in

    njegovo vsesplošno prisotnostjo.

    V začetku razvoja je bil glavni razlog za zmanjševanje velikosti podatkov cena

    pomnilniškega prostora, ki ni bil pretirano velik, datoteke pa so postajale vse večje (slike,

    zvočne datoteke, video). Nekoliko kasneje je prišel v ospredje prenos datotek preko

    omrežij, kjer je pomembna hitrost. Dandanes so cene nizke, hitrosti pa visoke, vendar je

    tudi podatkov bistveno več, ti pa so zaradi zviševanja kvalitete tudi vse večji. In ker je

    zmanjšanje podatkov na polovico prvotne velikosti ekvivalentno podvojitvi velikosti

    pomnilniškega prostora ali hitrosti prenosa, jih je smiselno obdelati, še posebej zato, ker je

    obdelava z modernimi procesorji izredno hitra. Stiskanje podatkov je tako v računalništvu

    nenehno prisotno.

    Algoritme za stiskanje podatkov delimo v dve skupini. V prvo sodijo algoritmi, ki pri

    stiskanju nekaj podatkov izgubijo (angl. lossy algorithms). Uporaba teh algoritmov je

    smiselna pri datotekah, kjer izguba podatkov nima usodnega pomena ali pa je sploh ni

    mogoče opaziti. Najpogosteje so to slikovne, zvočne in video datoteke. V drugo skupino

    sodijo algoritmi, ki podatke stisnejo brez izgub (angl. lossless algorithms). Ti so v glavnem

    manj učinkoviti, vendar si pri določenih vrstah datotek, kot na primer pri tekstovnih in

    izvršljivih datotekah, izgub ne smemo privoščiti. Obojim pa je skupno, da sestojijo iz

    kodirnika, s katerim podatke stisnemo, in dekodirnika, s katerim podatke povrnemo v

    prvotno obliko [11].

    Vsi algoritmi stiskanja podatkov temeljijo na izkoriščanju redundance, ki je prisotna pri

    nenaključnih podatkih, kar pomeni, da povečujejo entropijo podatkov. Preprosto stiskanje

    je tako že zamenjava daljših nizov istih simbolov. Tak niz lahko na primer zamenjamo s

    simbolom, kateremu sledi število ponovitev tega simbola. Drug preprost primer iz

    vsakdanjega življenja je krajšanje besed v pisnem komuniciranju, predvsem preko

    1

  • Izpeljanke algoritma LZW

    elektronskih naprav z omejitvijo dolžine sporočil, kjer recimo besedo »jutri« skrajšamo v

    »ju3«. Nekateri algoritmi podatke kodirajo glede na pogostost pojavljanja posameznih

    besed ali simbolov. Vzorce, ki se pojavljajo pogosteje, zapišemo s krajšimi kodami. Dober

    primer tega je Morsejeva abeceda [10].

    Načinov za stiskanje podatkov je veliko, v diplomskem delu pa bomo obravnavali

    algoritme, ki pri stiskanju podatkov uporabljajo slovar. Najprej bomo podrobneje predstavili

    stiskanje s slovarjem, nato pa opisali algoritem LZW ter tri izpeljanke, LZMW, LZAP in

    LZY. Za vsak algoritem bomo razložili postopek kodiranja in dekodiranja, ter delovanje

    predstavili na primeru. Opisali bomo njihovo implementacijo, kjer si bomo pomagali s

    psevdokodom. V zaključku diplomskega dela bomo algoritme primerjali med sabo,

    predstavili njihovo učinkovitost in podali mnenje ter končne ugotovitve.

    2

  • Izpeljanke algoritma LZW

    2 STISKANJE PODATKOV S SLOVARJEM

    Stiskanje podatkov s slovarjem temelji na zamenjavi nizov z žetoni oziroma kodami iz

    slovarja. Slovar vsebuje kode in njim dodeljene nize, ter je lahko vnaprej določen, statičen

    ali dinamičen. Če je slovar vnaprej določen, je podan kot del algoritma. V primeru

    statičnega slovarja kodirnik analizira vhodne podatke ter zgradi slovar, ki ga posreduje

    dekodirniku. V tem primeru je slovar shranjen skupaj s stisnjenimi podatki. Kodirnik in

    dekodirnik v primeru dinamičnega slovarja zgradita identičen slovar iz vhodnih podatkov,

    zato ta ni shranjen, ampak se v obeh primerih gradi od začetnega stanja.

    Enostaven primer stiskanja z vnaprej določenim slovarjem je stiskanje besedila v

    slovenskem jeziku s pomočjo slovarja, ki vsebuje pogoste slovenske besede. Slovar torej

    sestavljajo kode oziroma števila, ter njim dodeljene besede. Pri stiskanju besedila

    preberemo besedo, jo poiščemo v slovarju, in namesto nje zapišemo število. Če besede

    nismo našli, jo izpišemo nespremenjeno. Pri dekodiranju števila enostavno zamenjamo z

    besedami iz slovarja. To preprosto stiskanje se obnese pri povprečnem besedilu, vendar

    se nam zatakne že pri besedilih, ki vsebujejo manj pogoste besede. Če poznamo

    strukturo vhodnih podatkov, pa lahko izberemo prilagojen slovar. Tak način stiskanja je

    torej primeren za v naprej poznane podatke, ki jim lahko slovar prilagodimo, saj s tem

    dosegamo zelo dobre rezultate. Bolj splošno uporaben je način stiskanja s statičnim

    slovarjem, ki ga zgradi kodirnik. Najprej obdelamo besedilo in glede na pogostost

    pojavitev zgradimo slovar, ki ga nato uporabimo pri stiskanju in razširjanju. Tak pristop ima

    dve pomanjkljivosti. Prva je očitna, saj porabimo več časa, ko obdelujemo besedilo in

    gradimo slovar. Druga pa je del razširjanja, saj dekodirnik potrebuje slovar, ki ga je zgradil

    kodirnik. Tega je torej treba shraniti skupaj s stisnjenimi podatki, kar vpliva na velikost in s

    tem na razmerje stiskanja. V splošnem je tako najprimernejša uporaba dinamičnega

    slovarja. Ta se prilagaja glede na vhodne podatke, saj se kode in pripadajoči nizi v slovar

    dodajajo sproti. Začnemo z majhnim slovarjem, na primer z abecedo ali naborom ASCII,

    nato pa dodajamo nize, ki jih sestavimo iz vhodnih podatkov. Slovar ima omejeno število

    vnosov, zato ga ob zapolnitvi ponastavimo in začnemo graditi znova. Pri dekodiranju

    3

  • Izpeljanke algoritma LZW

    gradimo identičen slovar, vendar ravno obratno. Na ta način lahko v večini primerov

    stiskamo ostale vrste datotek enako uspešno kot tekstovne datoteke, zato je precej

    priljubljen [10].

    Algoritmi, ki uporabljajo slovar, spadajo v skupino entropičnih algoritmov in lahko

    načeloma pri dovolj velikih datotekah niz dolžine n simbolov stisnejo na velikost nH, kjer H

    predstavlja entropijo. Entropija je količina za merjenje informacije. Vsebnost informacije

    simbola v podatkih je v logaritemski odvisnosti od verjetnosti ponovitve simbola [10].

    Entropijo poljubnega niza izračunamo po enačbi 2.1:

    H (x)=∑i=1

    n

    P i log2Pi (2.1)

    Tu je:

    n – število simbolov

    Х – spremenljivka, ki vsebuje n vrednosti xi

    Pi – verjetnost simbola xi

    Algoritme, obravnavane v tem delu, lahko delimo glede na povečevanje vhodnega niza in

    dodajanja vnosov v slovar. Algoritem LZW napreduje po en simbol, in v slovar dodaja po

    en vnos za niz. Slovar se v tem primeru povečuje dokaj počasi, s tem pa je tudi

    prilagajanje vhodnim podatkom počasno. Ta problem skuša odpraviti algoritem LZMW, ki

    napreduje po več simbolov hkrati, kar pomeni, da v slovar dodaja daljše nize in se s tem

    bolje prilagaja vhodnim podatkom. Njegove pomanjkljivosti poskuša odpraviti algoritem

    LZAP, ki deluje podobno, vendar v slovar dodaja nov niz za vsak vhodni simbol, kar

    pomeni, da se prilagaja hitreje. Obstaja še četrta različica. To je algoritem LZY, ki niz

    povečuje za en simbol in za vsakega doda nov vnos v slovar [10].

    Preglednica 2.1: Razvrstitev algoritmov

    Povečava niza zaDodajanje v slovar

    Za niz Za simbolEn simbol: LZW LZY

    Več simbolov: LZMW LZAP

    4

  • Izpeljanke algoritma LZW

    3 ALGORITEM LZW

    Algoritem LZW sodi v družino popularnih algoritmov LZ in je izpeljan iz algoritma LZ78

    (občasno se zanj uporablja kratica LZ2), ki sta ga leta 1978 predstavila Abraham Lempel

    in Jacob Ziv [10]. Predhodnik tega algoritma je LZ77. LZW je leta 1984 razvil Terry Welch

    in ga tudi patentiral [10]. Algoritem je po izteku patenta postal popularen zaradi dobrih

    razmerij pri stiskanju različnih vrst podatkov in dokaj preproste implementacije.

    Stiskanje temelji na zamenjavi daljših nizov znakov s krajšimi kodami. Te nize sestavljamo

    iz vhodnih podatkov pri branju in jih dodajamo v slovar, ki sestoji iz začetnih in dodanih

    nizov ter njim dodeljenih kod. Začetni slovar je lahko implementiran iz različnih naborov

    znakov. Lahko je na primer napolnjen s črkami abecede, največkrat pa se uporablja nabor

    265 znakov tabele ASCII zaradi dolžine oziroma velikosti posameznega znaka, ki je 8

    bitov (1 zlog). Slovarja ni potrebno ohranjati, zato ga ne rabimo shranjevati skupaj s

    stisnjenimi podatki. Dolžina kod, ki jih dodajamo v slovar je lahko poljubna, vendar mora

    biti vsaj 9 bitov. Pogosto se uporabljata dolžini 12 in 16 bitov. Ko slovar napolnimo, ga

    izbrišemo in začnemo graditi znova [4][14].

    Algoritem mora pri dekodiranju graditi enak slovar, kot ga je gradil kodirnik, vendar je pri

    tem postopek bolj zapleten, saj se nizi v slovar dodajajo z zamikom. Tako se lahko zgodi,

    da nam niz s prebrano kodo še ni poznan, saj se še ne nahaja v slovarju. Na to posebnost

    bomo naleteli tudi v spodnjem primeru. Rešitev tega problema je preprosta, saj je le ta

    prisoten zgolj v primeru, da se niz začne in konča na isti znak, torej lahko na konec

    dodamo začetni znak in ta niz nato dodamo v slovar [2][6].

    3.1 KODIRANJE

    Prikažimo postopek kodiranja z vhodnim nizom ANABANANA (preglednica 3.1). Začetni

    slovar zaradi preglednosti inicializiramo s posameznimi znaki tega niza, tako da le ta

    vsebuje znake A, B in N. V preglednici 3.2 je začetno stanje slovarja ločeno od nadaljnjih

    5

  • Izpeljanke algoritma LZW

    vnosov v slovar s širšo črto. Kode jim dodelimo zaporedno, začnemo z 0. V slovar bomo

    tako nadaljevali z vnosi pri zaporedni številki 3.

    Začnemo z znakom A in preverimo, če se v slovarju nahaja niz AN, sestavljen iz

    trenutnega in naslednjega znaka. Ta niz še ni v slovarju, zato ga dodamo in mu dodelimo

    prosto zaporedno število, v tem primeru 3, ter na izhod zapišemo znak A oziroma kodo 0.

    Sedaj je trenutni znak N, niza NA pa prav tako ni v slovarju. Izpišemo N (kodo 2) in niz NA

    dodamo v slovar. Nadaljujemo po istem postopku, trenutni znak je A, ki ga izpišemo, saj

    niza AB še ni v slovarju. Niz dodamo v slovar pod zaporedno številko 5. Naslednji je na

    vrsti znak B. Ponovimo postopek, torej izpišemo B oziroma 1, ter dodamo niz BA v slovar.

    V naslednjem koraku je sestavljeni niz AN, tega pa smo že dodali v slovar. Tako niz AN

    postane trenutni niz, sestavljen niz ANA pa dodamo v slovar, ker ga tam še ni. Ker pa

    poznamo kodo za niz AN, lahko ta niz izpišemo kot 3. Nadaljujemo z A, in ponovno

    sestavimo AN, ki je v slovarju, zato je to naš trenutni niz. V naslednji iteraciji torej

    sestavimo niz ANA, ki je prav tako v slovarju. In ker smo dosegli konec (␄), temu nizu nemoremo dodati novega znaka, zato ga izpišemo.

    S tem smo zaključili s kodiranjem, naš stisnjen niz pa je 0, 2, 0, 1, 3, 7. Sestoji torej iz

    šestih znakov, medtem ko začetni niz vsebuje devet znakov (0, 2, 0, 1, 0, 2, 0, 2, 0).

    Začetni niz smo tako zmanjšali za tretjino.

    Preglednica 3.1: Postopek kodiranja z algoritmom LZW

    Trenutni niz Naslednji znak Izhod Vnos v slovar

    A N A (0) AN (3)

    N A N (2) NA (4)

    A B A (0) AB (5)

    B A B (1) BA (6)

    A N

    AN A AN (3) ANA (7)

    A N

    AN A

    ANA ␄ ANA (7)

    Preglednica 3.2: Slovar po koncu kodiranja

    Koda NizA 0

    B 1

    N 2

    AN 3

    NA 4

    AB 5

    BA 6

    ANA 7

    6

  • Izpeljanke algoritma LZW

    3.2 DEKODIRANJE

    Izhod kodiranja so kode 0, 2, 0, 1, 3, 7. Dekodiranje (preglednica 3.3) pričnemo z istim

    začetnim slovarjem, kot pri kodiranju. Ker pa sedaj gradimo slovar z zamikom, se nam

    lahko zgodi, da naletimo na kodo niza, ki je v slovarju še ni in bi bila dodana šele v istem

    koraku. To izjemo rešimo tako, da začetni znak niza dodamo tudi na konec.

    Na vhodu preberemo 0 in izpišemo znak A. Ker predhodnik ne obstaja, nadaljujemo z N.

    Sedaj lahko sestavimo niz AN in ker le tega še ni v slovarju, ga dodamo, znak pa

    izpišemo. Naslednji znak je A, sestavljeni niz pa NA, ponovno niz dodamo v slovar in

    izpišemo znak A. V naslednji iteraciji še enkrat ponovimo postopek, izpišemo torej B in AB

    dodamo v slovar. Nova prebrana koda je 3, torej gre za niz AN, ki ga izpišemo. Nov niz

    sedaj tvorimo iz predhodnega znaka in iz prvega znaka niza, torej je nov vnos v slovar niz

    BA. V zadnjem koraku pa naletimo na posebnost, saj prebrane kode 7 še ni v slovarju. Tja

    bi jo po običajnem postopku vpisali v tem koraku, ko bi sestavili niz ANA. To se zgodi v

    primeru, da se niz začne in konča na isti znak, torej v tem koraku na konec niza dodamo A

    in s tem tvorimo iskan niz, ki ga izpišemo in dodamo v slovar. S tem smo končali

    dekodiranje in obnovili začetni niz ANABANANA.

    Preglednica 3.3: Postopek dekodiranja z algoritmom LZW

    Vhod Predhodni niz Izhod Vnos v slovar

    0 A

    2 A N AN (3)

    0 N A NA (4)

    1 A B AB (5)

    3 B AN BA (6)

    7 AN ANA ANA (7)

    7

  • Izpeljanke algoritma LZW

    3.3 IMPLEMENTACIJA

    Za implementacijo slovarja pri kodiranju smo uporabili podatkovno strukturo map, kamor

    podatke shranimo v obliki para, ki sestoji iz ključa in se uporablja pri iskanju, ter vrednosti.

    Za dolžino kod smo izbrali 16 bitov (uint16_t), kar poenostavi zapisovanje podatkov

    (zapišemo lahko minimalno en zlog). Maksimalno število vnosov v slovar je tako 216

    (65536). Drugi pogost način je uporaba kod dolžine 12 bitov, kar pomeni 4096 vnosov v

    slovar. Niz smo definirali kot std::vector (v psevdokodu 3.1 je zapisan kot NIZ).

    Slovar je torej oblike std::map. Znak je tipa char in je v

    psevdokodu 3.1 predstavljen s c.

    Slika 3.1: Psevdokod kodiranja z algoritmom LZW

    Preverjanje napolnjenosti slovarja na slikah ni prikazano, saj za razumevanje poteka ni

    bistvenega pomena. V kodi je izvedeno s pogojem, ki enači kodo zadnjega niza z

    maksimalno velikostjo slovarja. Ob zapolnitvi se slovar v celoti izbriše in nato ponastavi na

    začetno stanje (na prvih 256 mest vnesemo znake tabele ASCII). Velikost slovarja je

    definirana s konstantno spremenljivko.

    Pri dekodiranju je slovar implementiran na nekoliko drugačen način in je oblike

    8

    WHILE ( lahko preberemo nov zlog v c ){ NIZ = NIZ + c // nizu dodamo zlog c

    IF ( NIZ ni v slovarju ) { slovar[NIZ] = naslednja prosta koda NIZ = NIZ - $ // nizu odstranimo zadnji znak PRINT slovar[NIZ] // izpišemo kodo za NIZ NIZ = c // NIZ inicializiramo na vrednost c }}

    if ( NIZ ni prazen ) PRINT slovar[NIZ]

  • Izpeljanke algoritma LZW

    std::vector. Niz je v psevdokodu 3.2 ponovno predstavljen z NIZ, koda

    pa je označena s K.

    Slika 3.2: Psevdokod dekodiranja z algoritmom LZW

    9

    WHILE ( lahko preberemo novo kodo v K ){ IF ( K == trenutna velikost slovarja ) // posebni primer { slovar[naslednja prosta koda] = NIZ + NIZ[0] } ELSE { IF ( NIZ ni prazen ) { slovar[naslednja prosta koda] = NIZ + slovar[K][0] } }

    PRINT slovar[K] NIZ = slovar[K]}

  • Izpeljanke algoritma LZW

    4 ALGORITEM LZMW

    Avtorja tega algoritma sta Victor Saul Miller in Mark Wegman, ime pa izvira iz začetnic

    priimkov [10]. Predstavljen je bil leta 1985. Cilj je izboljšati prilagajanje vhodnim

    podatkom, kar je izvedeno z dvema spremembama. Prva je brisanje najstarejšega vnosa

    v slovarju, ko se ta zapolni. Privzetih vrednosti ni dovoljeno brisati. Druga sprememba je

    pri dodajanju novih vnosov v slovar. Ti so sestavljeni iz ujemajočega niza in celotnega

    trenutnega niza. V tem se razlikuje od algoritma LZW, kjer ujemajočem nizu dodamo

    samo prvi znak trenutnega niza. S tem dosežemo dodajanje daljših nizov v slovar (celih

    besed ali pa celo besednih zvez), posledično pa tudi boljše prilagajanje vhodnim

    podatkom, saj so dodani nizi v bolj vsakdanji obliki [7][10].

    Algoritem ima tri slabosti. Zaradi oblike slovarja je podatkovna struktura manj preprosta

    kot pri LZW, prav tako pa se lahko isti niz v slovar doda dvakrat. Pri iskanju najdaljšega

    ujemajočega niza lahko pride do vračanja (angl. backtracking), kar še dodatno upočasni

    iskanje [10].

    4.1 KODIRANJE

    Postopek delovanja tudi tokrat prikažimo na nizu ANABANANA (preglednica 4.1). Začetno

    stanje slovarja ponovno inicializiramo s tremi simboli, ki sestavljajo niz. Končno stanje

    slovarja je prikazano v preglednici 4.2, začetno je ločeno z odebeljeno črto. Vsak korak

    pričnemo z iskanjem najdaljšega ujemajočega niza v slovarju. Prvi prebran znak je A, in

    ker v slovarju ni niza AN, je A ujemajoči niz, ki ga izpišemo s kodo 0. Naslednji najdaljši

    ujemajoči niz je N. Izpišemo kodo 2 in iz predhodnega niza sestavimo nov niz AN, ki ga

    dodamo v slovar s kodo 3. V naslednjem koraku je koda ponovno 0, sestavljen niz pa NA.

    Izpišemo torej 0 in v slovar dodamo niz NA s kodo 4. Postopek ponovimo s kodo 1 za

    vhodni simbol B in z vnosom AB s kodo 5 v slovar. V petem koraku pa je najdaljši

    ujemajoč niz AN s kodo 3. Sestavljen niz je BAN, s kodo 6. Ker smo prebrali dva znaka iz

    10

  • Izpeljanke algoritma LZW

    vhodnih podatkov, se za toliko mest tudi premaknemo. Naslednji znak je tako A, ponovno

    pa je najdaljši ujemajoč niz AN, zato spet izpišemo kodo 3. Sestavljen niz je tokrat ANAN.

    Opazimo lahko, da so sestavljeni nizi in s tem vnosi v slovar daljši kot pri LZW. Zadnji

    prebran znak je A, ker se pomaknemo za dve mesti. Izpišemo kodo 0, sestavljen niz ANA

    pa dodamo v slovar.

    S tem smo zaključili s kodiranjem, naš stisnjen niz pa je 0, 2, 0, 1, 3, 3, 0. Tokrat smo

    vhodni niz sicer zmanjšali iz 9 na 7 simbolov, vendar gre za kratek niz. Bolj pomembno je

    prikazati razliko v dolžini nizov v slovarju.

    Preglednica 4.1: Postopek kodiranja z algoritmom LZMW

    Predhodni niz Ujemajoč niz Sestavljen niz Vnos v slovar

    A (0) A

    A N (2) AN AN (3)

    N A (0) NA NA (4)

    A B (1) AB AB (5)

    B AN (3) BAN BAN (6)

    AN AN (3) ANAN ANAN (7)

    AN A (0) ANA ANA (8)

    Preglednica 4.2: Vnosi v slovar po končanem izvajanju algoritma LZMW

    Koda Niz

    A 0

    B 1

    N 2

    AN 3

    NA 4

    AB 5

    BAN 6

    ANAN 7

    ANA 8

    11

  • Izpeljanke algoritma LZW

    4.2 DEKODIRANJE

    Postopek dekodiranja, prikazan v preglednici 4.3, je praktično enak kot pri dekodiranju z

    algoritmom LZW, spremeni se zgolj dodajanje vnosov v slovar, ki je enako kot pri

    kodiranju. Zaradi drugače stisnjenih vhodnih podatkov in dodajanja nizov v slovar se

    posebno stanje pri dekodiranju ne pojavi.

    Preglednica 4.3: Postopek dekodiranja z algoritmom LZMW

    Vhod Predhodni niz Izhod Vnos v slovar

    0 (A) A

    2 (N) A N AN (3)

    0 (A) N A NA (4)

    1 (B) A B AB (5)

    3 (AN) B AN BAN (6)

    3 (AN) AN AN ANAN (7)

    0 (A) AN ANA ANA (8)

    4.3 IMPLEMENTACIJA

    Algoritem LZMW je za implementiranje zahtevnejši od ostalih treh, obravnavanih v tem

    delu, čeprav se ob pogledu na psevdokod 4.1 od njih bistveno ne razlikuje. Podatke v

    slovar dodajamo v daljših nizih, kar pomeni, da iskanje s povečevanjem za en simbol ne

    deluje. Ta problem lahko rešimo tako, da v podatkovno strukturo dodajamo tudi podnize,

    zraven pa vodimo še seznam, kateri vnosi v slovarju so namenjeni iskanju in kateri

    uporabi pri izpisovanju kod. Če se naslednji potencialni niz ne ujema, se je potrebno pri

    iskanju vračati nazaj na krajši niz v slovarju, kar še dodatno oteži implementacijo. Zaradi

    zahtevnosti implementacije te različice nismo implementirali in vključili v praktično

    primerjavo.

    12

  • Izpeljanke algoritma LZW

    Slika 4.1: Psevdokod kodiranja z algoritmom LZMW

    13

    WHILE ( i

  • Izpeljanke algoritma LZW

    5 ALGORITEM LZAP

    Ta algoritem je leta 1988 razvil James Storer in je nadgradnja algoritma LZMW [10]. Črki

    AP v imenu pomenita »all prefixes« (vse predpone), kar nam pove, kako poteka dodajanje

    v slovar. Dodajamo namreč več vnosov za eno ujemanje, te pa sestavimo iz predhodnega

    ujemajočega niza in trenutnega niza. Sestavimo jih tako, da predhodnemu nizu dodamo

    vse podnize trenutnega niza, kjer začnemo s prvim znakom in se pomikamo po en znak

    naprej. Tako recimo za predhodni niz SLO in trenutni niz VAR v slovar dodamo SLOV,

    SLOVA in SLOVAR [10].

    Slovar se pri tej različici povečuje hitreje, kar pomeni hitrejše prilagajanje podatkom in s

    tem nekoliko boljše razmerje stiskanja. Tudi iskanje v slovarju poteka hitreje, ker se ni

    treba vračati pri iskanju najdaljšega ujemanja [1].

    5.1 KODIRANJE

    Postopek je precej podoben tistemu pri algoritmu LZMW, glavna razlika je pri vnosih v

    slovar. Prikaz delovanja je v preglednici 5.1. Ponovno smo izbrali niz ANABANANA,

    izhodni podatki pa so v tem primeru enaki tistim iz poglavja 4.1. Prva razlika v postopku je

    v petem koraku, kjer je predhodni niz B in trenutni oziroma ujemajoči niz AN. Sestavimo

    torej niza BA in BAN s kodama 6 in 7. V naslednjem koraku sta predhodni in ujemajoči niz

    enaka, sestavimo pa niz ANA, ki ga v slovar dodamo s kodo 8, ter ANAN s kodo 9. S tem

    smo že presegli končno število vnosov v slovar pri algoritmu LZW (7), pa tudi algoritmu

    LZMW (8). Na koncu ponovno sestavimo niz ANA, ki pa je že v slovarju, zato ga ne

    dodamo.

    14

  • Izpeljanke algoritma LZW

    Preglednica 5.1: Postopek kodiranja z algoritmom LZAP

    Predhodni niz Ujemajoč niz Sestavljen niz Vnos v slovar

    A (0) A

    A N (2) AN AN (3)

    N A (0) NA NA (4)

    A B (1) AB AB (5)

    B AN (3) BAN BA (6), BAN (7)

    AN AN (3) ANAN ANA (8), ANAN (9)

    AN A (0) ANA

    Preglednica 5.2: Vnosi v slovar po končanem izvajanju algoritma LZAP

    Koda NizA 0

    B 1

    N 2

    AN 3

    NA 4

    AB 5

    BA 6

    BAN 7

    ANA 8

    ANAN 9

    5.2 DEKODIRANJE

    Postopek dekodiranja je ponovno zelo podoben tistemu pri algoritmih LZW ter LZMW in je

    prikazan v preglednici 5.3. Tudi tokrat se spremeni zgolj dodajanje vnosov v slovar.

    Posebno stanje se pri tem algoritmu ne pojavi.

    15

  • Izpeljanke algoritma LZW

    Preglednica 5.3: Postopek dekodiranja z algoritmom LZAP

    Vhod Predhodni niz Izhod Vnos v slovar

    0 (A) A

    2 (N) A N AN (3)

    0 (A) N A NA (4)

    1 (B) A B AB (5)

    3 (AN) B AN BA (6), BAN (7)

    3 (AN) AN AN ANA (8), ANAN (9)

    0 (A) AN ANA

    5.3 IMPLEMENTACIJA

    Za slovar smo izbrali podatkovno strukturo map, za dolžine kod pa 16 bitov. Oblika

    slovarja je std::map. Podatkovno strukturo deque smo izbrali,

    ker smo potrebovali enostaven dostop tudi do začetka nizov. Gre za obojestransko vrsto

    (angl. double ended que). Pri iskanju najdaljšega ujemanja smo prebrali simbol, nato pa

    dodajali po en simbol in vsakič preverjali ujemanje. Pri tem nam je bila v veliko pomoč

    funkcija unget(), s katero smo se lahko po vhodnem toku podatkov premaknili za en

    simbol nazaj, ko smo naleteli na neujemanje. Pri dodajanju vnosov v slovar smo morali biti

    pozorni na zapolnitev pri vsakem novem vnosu (slednje v psevdokodu 5.1 ni prikazano).

    Vnosi se dodajajo v zanki in povečujejo za en simbol. Zraven smo preverjali še prisotnost

    niza v slovarju, da ne pride do podvojitve vnosov. Prav tako smo morali biti pozorni na

    konec vhodnega toka podatkov pri iskanju najdaljšega ujemanja. Če dosežemo konec

    med iskanjem najdaljšega ujemanja, izpišemo samo zadnji prebran simbol.

    Dekodiranje poteka precej podobno kot pri algoritmu LZW, vendar smo tukaj zaradi

    preprečevanja podvojitve vnosov potrebovali še možnost iskanja po slovarju, zato smo

    vodili dva identična slovarja, implementirana z dvema podatkovnima strukturama. Slovar,

    ki smo ga uporabljali za izpisovanje, je oblike std::vector, za iskanje

    pa std::map. Tudi pri dekodiranju smo potrebovali dostop do

    začetka niza. Za razliko od kodiranja, kjer smo izpisovali kode, tu izpisujemo nize, kar

    nam je zaradi dodajanja na začetek otežilo uporabo podatkovne strukture deque, zato

    16

  • Izpeljanke algoritma LZW

    smo uporabili vector. Problem se pri uporabi obojestranske vrste pojavi pri zapisovanju v

    datoteko, kjer izpisujemo nize tako, da podamo njihov začetek in velikost. Ker ta struktura

    ne zagotavlja pravilnega zaporedja elementov lahko pride do popačenja izhodnih

    podatkov.

    Slika 5.1: Psevdokod kodiranja z algoritmom LZAP

    17

    WHILE ( lahko preberemo nov zlog v c ){ input = input + c

    DO // iskanje najdaljšega ujemanja { preberemo naslednji simbol cn input = input + cn } WHILE ( lahko najdemo input v slovarju )

    v vhodnem toku se premaknemo za en simbol nazaj input = input - $ // odstanimo zadnji zlog longest_match = input prev_match = input

    PRINT slovar[longest match] // izpišemo kodo

    DO // dodajanje novih nizov { d = d + longst_match[0] REMOVE longest_match[0] // odstranimo prvi zlog IF ( d ni v slovarju ) { slovar[naslednja prosta koda] = d } } WHILE ( longest_match ni prazen niz )

    d = prev_match}

  • Izpeljanke algoritma LZW

    6 ALGORITEM LZY

    Algoritem LZY je četrta izpeljanka algoritma LZW, ki smo jo obravnavali v diplomskem

    delu. Avtor je Daniel J. Bernstein, ki je algoritem iznašel 26. decembra 1990 [1]. Črka Y v

    imenu je iz niza Yabba, ki je del niza, s katerim je bil algoritem prvotno testiran.

    Pri kodiranju in gradnji slovarja si pomagamo s trenutnim najdaljšim ujemanjem, ki je na

    začetku prazno. Ko preberemo nov simbol, pred dodajanjem v slovar preverimo, če je

    najdaljše ujemanje skupaj s prebranim simbolom že v slovarju, zgrajenem pred začetkom

    najdaljšega ujemanja. Če je temu tako, je nov niz trenutno najdaljše ujemanje. V

    nasprotnem primeru izpišemo kodo najdaljšega ujemanja in tega ponastavimo na samo

    prebran simbol. V slovar dodamo nov niz, odstranimo prvi simbol tega niza in ponovimo ta

    korak. Pri dekodiranju preberemo kodo in v slovarju poiščemo pripadajoč niz. Nato

    poiščemo prvi simbol tega niza in posodobimo slovar na enak način kot pri kodiranju. Ta

    simbol izpišemo in ga odstranimo iz začetka niza. Ta korak ponovimo, dokler lahko iz niza

    preberemo prvi znak. Nadaljujemo z naslednjo kodo, dokler ne dekodiramo vhodnih

    podatkov [1].

    6.1 KODIRANJE

    Ker izhodni podatki niso sinhronizirani z dodajanjem nizov v slovar, tega razdelimo na dva

    dela. Prvi del vsebuje nize, ki jih lahko izpišemo, v drugega pa začasno dodajamo nove, ki

    jih kasneje premaknemo v prvega. To je podrobneje opisano v poglavju 6.3, v tem prikazu

    pa smo dodajanje v slovar zaradi preglednosti nekoliko poenostavili. V preglednici 6.1

    smo uporabili spremenljivke c, oc, o, mc in m. S c smo označili vhodni simbol, oc

    predstavlja sestavljen niz iz o in c, medtem ko je o pomožni niz. Ker so vsi podnizi

    trenutnega najdaljšega ujemanja prav tako v slovarju, lahko shranjujemo zgolj tega, za kar

    uporabimo m. V mc shranimo sestavljen niz iz m in c. Ponovno prikažimo delovanje z

    nizom ANABANANA. Slovar na začetku vsebuje A, B in N (preglednica 6.2 nad

    18

  • Izpeljanke algoritma LZW

    odebeljeno črto). Ostale spremenljivke so prazne. Pričnemo s prvim znakom vhoda.

    Preberemo simbol A in sestavimo niz oc, ki je prav tako A, saj je o na začetku prazen. Tudi

    spremenljivki mc in m sta v tem koraku A. Naslednji prebran simbol je N, sestavljen niz oc

    pa AN. Ker oc ni v slovarju, izpišemo kodo za o (ta je A, torej 0) in v o shranimo c.

    Sestavljen niz mc je AN. V vsakem koraku temu nizu odstranimo prvo črko in ponovno

    preverimo, če se nahaja v slovarju (če se ne, ga dodamo). To ponavljamo, dokler niz ni v

    slovarju. Nato v m shranimo mc. V slovar smo tako v tem koraku dodali AN s kodo 3, v m

    pa je sedaj shranjen N. V naslednjih treh korakih je postopek enak, izpišemo pa 2 (N), 0

    (A), in 1 (B), ter v slovar dodamo NA (4), AB (5) in BA (6). Nato preberemo N, oc pa je

    sedaj niz, ki že obstaja v slovarju, zato v o shranimo oc. Tudi mc je v slovarju, zato v m že

    takoj shranimo mc, ki je AN. V naslednjem koraku preberemo A in sestavimo ANA. Ker

    tega niza ni v slovarju, v o shranimo c. Izpišemo AN (3), sestavimo mc (ANA), ter v slovar

    dodamo ANA (7), m pa postane AN (najdaljše ujemanje s slovarjem). V naslednjem

    koraku je sestavljen niz ponovno AN, mc pa je tokrat NAN (v slovar ta niz dodamo s kodo

    8). Najdaljše ujemanje (m) postane AN. Sledi še zadnji simbol iz vhoda, A. Sestavimo

    ANA, tako da je o sedaj A, izpišemo 3 (AN), mc in m pa sta prav tako ANA. Sedaj na

    vhodu več ni podatkov, zato zgolj izpišemo kodo za o (0).

    Izhodni niz sestoji iz kod 0, 2, 0, 1, 3, 3, 0. Zaradi kratkega vhodnega niza je rezultat enak

    kot pri algoritmih LZMW in LZAP. Končni slovar ima tokrat 8 vnosov.

    Preglednica 6.1: Postopek kodiranja z algoritmom LZY

    Vhod (c) oc o Izhod mc m Vnos v slovar

    A A A A A

    N AN N 0 (A) AN N AN (3)

    A NA A 2 (N) NA A NA (4)

    B AB B 0 (A) AB B AB (5)

    A BA A 1 (B) BA A BA (6)

    N AN AN AN AN

    A ANA A 3 (AN) ANA NA ANA (7)

    N AN AN NAN AN NAN (8)

    A ANA A 3 (AN) ANA ANA

    ␄ 0 (A)

    19

  • Izpeljanke algoritma LZW

    Preglednica 6.2: Vnosi v slovar po končanem izvajanju algoritma LZY

    Koda NizA 0

    B 1

    N 2

    AN 3

    NA 4

    AB 5

    BA 6

    ANA 7

    NAN 8

    6.2 DEKODIRANJE

    Postopek dekodiranja je enak kot pri kodiranju. Začetni slovar in spremenljivke so enaki.

    Preberemo prvo kodo in v slovarju poiščemo pripadajoči niz. Nato poiščemo prvi simbol v

    nizu in gradimo slovar kot smo to počeli pri kodiranju, dokler niz ni prazen. Prav tako

    izpišemo c in ga odrežemo od začetka niza. Prva koda je 0, kar pomeni, da smo prebrali

    A. Prvi simbol je torej A, mc in m prav tako. Naslednji prebran niz in prvi simbol tega niza

    je N (koda 2). Tako kot pri kodiranju sestavimo mc, ki je AN, ter preverimo, če je v slovarju.

    Ker ga tam ni, ga dodamo (koda 3), nato pa odstranimo prvi simbol in ponovno preverimo

    prisotnost v slovarju. Ker je N v slovarju, je to sedaj najdaljši ujemajoč niz. Sedaj izpišemo

    c in ga odstranimo iz začetka niza o. Pri naslednjih dveh korakih je postopek enak. V

    naslednjem preberemo niz AN (koda 3). Prvi simbol c je A, sestavljen simbol (mc) pa BA

    (v prejšnjem koraku je m bil B). V slovar dodamo BA s kodo 6, najdaljši ujemajoč niz (m)

    pa je A. Izpišemo c (A) in ta simbol odstranimo iz začetka o, kjer je sedaj samo N. Ker o

    še ni prazen, v naslednjem koraku ne preberemo nove kode, ampak ponovimo postopek,

    le da je sedaj v c shranjen N. Sestavljen niz mc je tako AN, ki pa je v slovarju, zato tokrat

    ne dodamo novega vnosa. Naslednja koda je ponovno 3 (AN), torej je c sedaj A, mc pa

    ANA. Tega niza še ni v slovarju, zato ga s kodo 7 tja dodamo, m pa postane NA. Izpišemo

    c in posodobimo o, ki še ni prazen, zato je v novem koraku c N, nove kode pa ne

    preberemo. Sestavimo mc, ki je NAN. Niz dodamo v slovar (koda 8), m postane AN,

    20

  • Izpeljanke algoritma LZW

    izpišemo pa c (N). V zadnjem koraku preberemo kodo 0. Sestavimo niz ANA, ki je v

    slovarju, zato je tudi m sedaj ANA, v slovar pa ne dodamo ničesar. Izpišemo A (c), ker pa

    novih kod na vhodu ni več, smo z dekodiranjem končali.

    Preglednica 6.3: Postopek dekodiranja z algoritmom LZY

    Vhod (o) c mc m Vnos v slovar Izhod

    0 (A) A A A A

    2 (N) N AN N AN (3) N

    0 (A) A NA A NA (4) A

    1 (B) B AB N AB (5) B

    3 (AN) A BA A BA (6) A

    N AN AN N

    3 (AN) A ANA NA ANA (7) A

    N NAN AN NAN (8) N

    0 (A) A ANA ANA A

    6.3 IMPLEMENTACIJA

    Za dolžino kod smo, enako kot pri ostalih implementacijah, izbrali 16 bitov, slovar za

    postopek kodiranja pa smo implementirali kot std::map.

    Tokrat smo potrebovali še pomožen slovar, ki je prav tako podatkovna struktura

    std::map. Spremenljivki o in m sta std::vector, c pa

    char. Postopek ponastavljanja slovarja v psevdokodu 6.1 ni prikazan, je pa nekoliko

    zahtevnejši, kot pri ostalih treh algoritmih, saj je slovar sestavljen iz dveh delov, prav tako

    pa izhod ni sinhroniziran z dodajanjem v slovar. V literaturi ta postopek ni opisan, zato

    smo pri implementaciji imeli proste roke. Ob zapolnitvi slovarja je v začasnem slovarju

    lahko več nizov, pozorni pa moramo biti na že izpisane kode. Najpreprostejši način je bil

    uporaba števcev za prebrane in izpisane kode, ob zapolnitvi slovarja pa iz konca

    najdaljšega ujemanja izpišemo toliko simbolov, kot je razlika med števcema, nato pa vse

    spremenljivke skupaj s slovarjem ponastavimo.

    21

  • Izpeljanke algoritma LZW

    Slika 6.1: Psevdokod kodiranja z algoritmom LZY

    Pri implementiranju dekodiranja smo pri tem algoritmu zaradi iskanja tako kod kot nizov

    potrebovali dva slovarja, implementirana z različnimi podatkovnimi strukturami. Za prvega

    smo uporabili std::vector, za drugega pa std::map. Drugi možni način bi bil uporaba podatkovne strukture bimap. Ob zapolnitvi

    slovarjev oba ponastavimo na začetno vrednost, kar pa v psevdokodu 6.2 ni prikazano,

    ker ni ključno za razumevanje algoritma. Dodajanje vnosov v slovar je izvedeno z enako

    zanko, kot pri kodiranju, le da v tem primeru zaradi uporabe dveh slovarjev nov vnos

    dodamo v oba.

    22

    slovar = začetni slovarT = pomožni slovaro = m = prazen niz

    WHILE ( lahko preberemo nov zlog v c ){ IF ( slovar vsebuje oc ) { o = oc } ELSE { PRINT slovar[o] o = c slovar = Slovar + T CLEAR T // izpraznimo T }

    WHILE ( mc ni v slovar ali T ) { T[naslednja prosta koda] = mc REMOVE m[0] // izbrišemo prvi simbol v m }

    m = mc}

    PRINT slovar[o]

  • Izpeljanke algoritma LZW

    Slika 6.2: Psevdokod dekodiranja z algoritmom LZY

    23

    WHILE ( lahko preberemo novo kodo k ){ o = Slovar[k] // poiščemo niz, ki ga koda predstavlja

    WHILE ( o ni prazen niz ) { c = o[0] // v c shranimo prvi simbol niza o

    WHILE ( mc ni v slovarju ) { Slovar[naslednja prosta koda] = mc REMOVE m[0] // izbrišemo prvi simbol v m }

    PRINT c // izpišemo c REMOVE o[0] // odstanimo prvi simbol v nizu o }}

  • Izpeljanke algoritma LZW

    7 REZULTATI

    Algoritme smo testirali na šestih datotekah, ki se razlikujejo po tipu in velikosti. Izbrali smo

    slikovno (.bmp), izvršljivo oziroma binarno (.exe) in zvočno (.mp3) datoteko ter štiri

    tekstovne (.txt) datoteke:

    • slika lenna.bmp v velikosti 463 KB, standardna slikovna datoteka namenjena

    testiranju, fotografija Lene Söderberg [5],

    • binarna datoteka putty.exe v velikosti 512 KB, SSH in Telnet klient [8],

    • zvočna datoteka step.mp3 v velikosti 207 KB, posnetek govora Neila Armstronga

    ob prvem pristanku na Luni [12],

    • frankenstain.txt v velikosti 419 KB, Frankenstein, novela Mary Shelley [3],

    • republic.txt v velikosti 680 KB, Platonov sokratski dialog Država [9] in

    • war_and_peace.txt v velikosti 3194 KB (v nadaljevanju war.txt), Vojna in mir,

    roman Leva N. Tolstoja [13].

    Zvočna datoteka je v stisnjenem formatu, da smo lahko preverili stiskanje na podatkih z

    nizko redundanco.

    Algoritme smo testirali s procesorjem i5-4670K (takt ure 4 GHz) in 1600 Hz pomnilnikom.

    Implementirani so v razvojnem okolju Microsoft Visual Studio 2015 na operacijskem

    sistemu Windows 10. V nadaljevanju smo v preglednicah prikazali rezultate algoritmov za

    vsako datoteko. Izmerili smo čas kodiranja ter dekodiranja, razmerje stiskanja (enačba

    7.1) in faktor stiskanja (enačba 7.2):

    Razmerje stiskanja=Velikost izhodnih podatkovVelikost vhodnih podatkov (7.1)

    Faktor stiskanja= Velikost vhodnih podatkovVelikost izhodnih podatkov (7.2)

    24

  • Izpeljanke algoritma LZW

    0,00

    2,00

    4,00

    6,00

    8,00

    10,00

    12,00

    14,00

    LZWLZAPLZYČ

    as (s

    )

    Slika 7.2: Grafikon primerjave porabljenega časa pri kodiranju

    25

    0,5

    1,0

    1,5

    2,0

    2,5

    LZWLZAPLZY

    Fakt

    or s

    tiska

    nja

    Slika 7.1: Grafikon primerjav faktorjev stiskanja algoritmov

  • Izpeljanke algoritma LZW

    0,00

    1,00

    2,00

    3,00

    4,00

    5,00

    6,00

    LZWLZAPLZYČ

    as (s

    )

    Slika 7.3: Grafikon primerjave porabljenega časa pri dekodiranju

    7.1 LZW

    Kljub temu, da med algoritmi ni večjih razlik pri uspešnosti stiskanja, se je v večini

    primerov algoritem LZW obnesel najbolje. Razlog za to je ponastavitev slovarja ob

    zapolnitvi pri vseh implementiranih algoritmih. Zaradi preproste implementacije je tudi zelo

    hiter, saj ne iščemo najdaljših ujemanj. Tako kot ostali algoritmi se je dobro odnesel pri

    stiskanju tekstovnih datotek, ne glede na njihovo dolžino. Najmanj uspešen, tako kot ostali

    algoritmi, je bil pri stiskanju zvočne datoteke, saj je tam entropija zaradi že obdelanih

    podatkov nizka. V primerjavi z ostalimi algoritmi je najslabše stisnil binarno datoteko, ker

    se na vhodne podatke prilagaja počasneje. Pri stiskanju slikovne datoteke med algoritmi

    ni bistvene razlike. Uspešnost stiskanja bi lahko izboljšali, če bi spremenili način brisanja

    vnosov iz slovarja. Ob zapolnitvi bi tako lahko na primer brisali zadnji uporabljen vnos, ali

    pa bi na mesto da slovar brišemo, sproti preverjali razmerje stiskanja in bi ga ponastavili

    zgolj takrat, ko bi razmerje padlo pod določeno mejo.

    26

  • Izpeljanke algoritma LZW

    Preglednica 7.1: Rezultati algoritma LZW

    DatotekaČas

    kodiranja (s)Čas

    dekodiranja (s)Razmerje Faktor Zmanjšanje (%)

    lenna.bmp 0,5529 0,0638 0,64 1,55 35,64putty.exe 0,4245 0,0857 0,80 1,26 20,31step.mp3 0,2089 0,0424 0,95 1,05 4,83frankenstain.txt 0,3123 0,0414 0,45 2,23 55,13republic.txt 0,5004 0,0595 0,41 2,41 58,53war.txt 2,3397 0,2836 0,43 2,31 56,64

    7.2 LZAP

    Izredno hitro prilagajanje algoritma LZAP je lahko prednost, vendar se lahko izkaže tudi

    kot slabost. Rezultati kažejo, da se pri stiskanju ni izkazal za najboljšega, čeprav je v

    teoriji pri stiskanju uspešnejši od algoritma LZW. Glavni razlog je brisanje celotnega

    slovarja. Brisanje najstarejših vnosov omogoča boljše prilagajanje vhodnim podatkom ob

    zapolnitvi, do katere pri tem algoritmu pride prej, kot pri ostalih. S tem, ko se slovar

    pogosto gradi od začetka, se na izhod izpisujejo krajši nizi, posledično pa več kod, kar

    pomeni večjo končno datoteko. V primerjavi z algoritmom LZW bi LZAP bistveno bolje

    stisnil datoteko, v kateri je večje število ponovitev istega simbola. Pri stiskanju zvočne

    datoteke je bila stisnjena oblika podatkov večja od prvotne, vendar je tudi pri ostalih

    algoritmih faktor stiskanja približno 1. Algoritem za kodiranje podatkov porabi več časa kot

    ostali algoritmi, saj iskanje najdaljšega ujemanja precej vpliva na hitrost. Dekodiranje je

    kljub temu hitro, ker zgolj preverjamo, če je vnos že v slovarju. Izboljšanje hitrosti izvajanja

    bi lahko dosegli s implementacijo bolj prilagojene podatkovne strukture.

    27

  • Izpeljanke algoritma LZW

    Preglednica 7.2: Rezultati algoritma LZAP

    DatotekaČas

    kodiranja (s)Čas

    dekodiranja (s)Razmerje Faktor Zmanjšanje (%)

    lenna.bmp 2,9015 0,8641 0,65 1,54 35,21putty.exe 2,6698 0,7087 0,78 1,29 22,27step.mp3 1,2646 0,3032 1,02 0,98 -1,93frankenstain.txt 1,5611 0,4706 0,47 2,10 52,51republic.txt 2,5573 0,7953 0,44 2,27 56,03war.txt 12,0249 3,7199 0,47 2,15 53,44

    7.3 LZY

    Algoritem LZY je izmed vseh najbolje stisnil binarno, malenkost bolje pa tudi slikovno

    datoteko. Pri tekstovnih datotekah ni veliko zaostajal za algoritmom LZW, vendar smo tudi

    tukaj brisali celoten slovar. Čas izvajanja kodiranja je nekoliko boljši, kot pri algoritmu

    LZAP. Večja razlika v trajanju izvajanja pa je pri dekodiranju, kjer se je algoritem izkazal za

    najpočasnejšega. Razlog za to je postopek gradnje slovarja, ki se pri tem algoritmu izvaja

    na enak način pri kodiranju in dekodiranju. Pri optimalni implementaciji naj bi v teoriji ta

    algoritem bil v splošnem nekoliko uspešnejši od algoritma LZW, vendar bi bil ne glede na

    to še vedno počasnejši, saj ga ni mogoče pohitriti do te mere, da bi se po hitrosti primerjal

    z LZW [1].

    Preglednica 7.3: Rezultati algoritma LZY

    DatotekaČas

    kodiranja (s)Čas

    dekodiranja (s)Razmerje Faktor Zmanjšanje (%)

    lenna.bmp 1,4540 1,1672 0,64 1,56 35,85putty.exe 1,4286 0,9917 0,76 1,31 23,63step.mp3 0,6037 0,4456 0,99 1,01 0,48frankenstain.txt 0,8322 0,6687 0,46 2,19 54,41republic.txt 1,3744 1,1154 0,43 2,34 57,35war.txt 6,4152 5,1279 0,45 2,21 54,92

    28

  • Izpeljanke algoritma LZW

    8 SKLEP

    V diplomskem delu smo prikazali osnovne koncepte ter prednosti in slabosti algoritmov, ki

    pri stiskanju podatkov uporabljajo slovar. Predstavili smo postopke za algoritem LZW in

    njegove izpeljanke, ter razlike med njimi. Ugotovili smo, da niso primerni za stiskanje vseh

    vrst datotek, saj je pri nekaterih stisnjena različica celo večja od prvotne. Spet pri drugih je

    razmerje stiskanja nezadovoljivo. Bolj kot za naključne so ti algoritmi primerni za podatke,

    kjer je ponavljanje vzorcev večje.

    Pri implementiranju smo pri vseh algoritmih slovar ob zapolnitvi v celoti ponastavili, za to

    so rezultati pri algoritmih LZAP in LZY v povprečju nekoliko slabši od rezultatov algoritma

    LZW. Pri teh algoritmih je ob zapolnitvi slovarja priporočeno brisanje najstarejših vnosov.

    Druga možna izboljšava je brisanje slovarja, ko razmerje stiskanja pade pod zadovoljivo

    mejo. Za enako ravnanje s slovarjem pri vseh algoritmih smo se odločili, ker lahko

    izboljšave implementiramo tudi pri algoritmu LZW in s tem vplivamo na uspešnost

    stiskanja. Primerjavo algoritmov bi lahko razširili še z implementacijo slovarjev za različne

    dolžine kod. Pri stiskanju manjših datotek bi razmerja zaradi krajših kod bila nekoliko

    boljša, ker večjega slovarja ne bi zapolnili, kode pa bi bile daljše. Največja razlika med

    algoritmi pa je vidna pri hitrosti izvajanja. Algoritmi, pri katerih je prisotnega več iskanja po

    slovarju ter obdelave podatkov so počasnejši, prav tako pa zahtevnejši za implementacijo.

    Prednost algoritma LZW je torej njegova preprosta oblika, posledica katere je hitro

    izvajanje in enostavnejša implementacija, kar je bistvenega pomena za njegovo

    popularnost in množično uporabo.

    29

  • Izpeljanke algoritma LZW

    VIRI

    [1] Bernstein, D. J. Y coding: Draft 4b. 19 marec, 1991

    [2] Bhat, S. LZW Data Compression. 2002. Dostopno na:

    https://www.cs.duke.edu/csed/curious/compression/lzw.html [28.7.2016]

    [3] frankenstain.txt, Dostopno na: https://www.gutenberg.org/ebooks/84 [25.8.2016]

    [4] Lelewer, D. A., Hirschberg, D. S. Data Compression. Poglavje 5.1. Dostopno na:

    https://www.ics.uci.edu/~dan/pubs/DC-Sec5.html#Sec_5.1 [8.8.2016]

    [5] lenna.bmp. Dostopno na: http://www.hlevkin.com/TestImages/lenna.bmp

    [25.8.2016]

    [6] Marshall, D. Lossless Compression Algorithms. 2001. Dostopno na:

    https://www.cs.cf.ac.uk/Dave/Multimedia/node214.html [3.8.2016]

    [7] Miller Victor S., Wegman Mark N. Variations on a theme by Ziv and Lempel,

    Research Report, 1984

    [8] putty.exe. Dostopno na: https://the.earth.li/~sgtatham/putty/latest/x86/putty.exe

    [25.8.2016]

    [9] republic.txt. Dostopno na: http://classics.mit.edu/Plato/republic.mb.txt [25.8.2016]

    [10] Salomon D., Motta G., Handbook of Data Compression, 5. izdaja. New York:

    Springer, 2010

    [11] Smith S. W., Chapter 27 V: The Scientist and Engineer's Guide to Digital Signal

    Processing. California Technical Pub, 1997, str. 481-502

    [12] step.mp3, Dostopno na:

    https://www.nasa.gov/mp3/590331main_ringtone_smallStep.mp3 [25.8.2016]

    [13] war_and_peace.txt, Dostopno na: https://www.gutenberg.org/ebooks/2600

    [25.8.2016]

    [14] Welch T. A., A Technique for High-Performance Data Compression. Computer,

    vol.17, št. 6, str. 8-19, junij 1984

    30

    1 UVOD2 STISKANJE PODATKOV S SLOVARJEM3 ALGORITEM LZW3.1 KODIRANJE3.2 DEKODIRANJE3.3 IMPLEMENTACIJA

    4 ALGORITEM LZMW4.1 KODIRANJE4.2 DEKODIRANJE4.3 IMPLEMENTACIJA

    5 ALGORITEM LZAP5.1 KODIRANJE5.2 DEKODIRANJE5.3 IMPLEMENTACIJA

    6 ALGORITEM LZY6.1 KODIRANJE6.2 DEKODIRANJE6.3 IMPLEMENTACIJA

    7 REZULTATI7.1 LZW7.2 LZAP7.3 LZY

    8 SKLEP