Skip to content
Snippets Groups Projects
Ladislav_Zemek_BP_UTF-8.tex 70.8 KiB
Newer Older
Ladislav Zemek's avatar
Ladislav Zemek committed
% arara: pdflatex
% arara: pdflatex
% arara: pdflatex

% options:
% thesis=B bachelor's thesis
% thesis=M master's thesis
% czech thesis in Czech language
% slovak thesis in Slovak language
% english thesis in English language
% hidelinks remove colour boxes around hyperlinks

\documentclass[thesis=B,czech]{FITthesis}[2012/06/26]

\usepackage[utf8]{inputenc} % LaTeX source encoded as UTF-8

\usepackage{graphicx} %graphics files inclusion
Ladislav Zemek's avatar
Ladislav Zemek committed
\usepackage[]{algorithm2e}
Ladislav Zemek's avatar
Ladislav Zemek committed
\usepackage{listings}
Ladislav Zemek's avatar
Ladislav Zemek committed
\usepackage{float}
Ladislav Zemek's avatar
Ladislav Zemek committed
% \usepackage{amsmath} %advanced maths
% \usepackage{amssymb} %additional math symbols

\usepackage{dirtree} %directory tree visualisation

% % list of acronyms
% \usepackage[acronym,nonumberlist,toc,numberedsection=autolabel]{glossaries}
% \iflanguage{czech}{\renewcommand*{\acronymname}{Seznam pou{\v z}it{\' y}ch zkratek}}{}
% \makeglossaries

\newcommand{\tg}{\mathop{\mathrm{tg}}} %cesky tangens
\newcommand{\cotg}{\mathop{\mathrm{cotg}}} %cesky cotangens
Ladislav Zemek's avatar
Ladislav Zemek committed
\newcommand{\classname}[1]{\texttt{#1}}
Ladislav Zemek's avatar
Ladislav Zemek committed
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% ODTUD DAL VSE ZMENTE
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % 

\department{Katedra softwarového inženýrství}
\title{Implementace kompresních metod LZ77, LZ78 a~LZW v~jazyce Java}
\authorGN{Ladislav} %(křestní) jméno (jména) autora
\authorFN{Zemek} %příjmení autora
\authorWithDegrees{Ladislav Zemek} %jméno autora včetně současných akademických titulů
\author{Ladislav Zemek} %jméno autora bez akademických titulů
\supervisor{Ing. Radomír Polách}
\acknowledgements{Rád bych poděkoval Ing. Radomírovi Poláchovi za pomoc a vstřícnost při vedení bakalařské práce.}
Ladislav Zemek's avatar
Ladislav Zemek committed
\abstractCS{Tato bakalářská práce se~zabývá návrhem, analýzou, implementací a~testováním tří kompresních metod LZ77, LZ78 a~LZW do~knihovny SCT. Knihovna je koncipována tak, aby v~budoucnu nabídla uživatelům velkou škálu kompresních metod a~tak usnadnila výběr vhodné metody pro jejich projekt.  Uvnitř této práce se zabývám vhodným návrhem a~popisem daných algoritmů. U~všech metod se mi podařilo při vhodně zvolených parametrech dosáhnout zmenšení testovaných korpusů vždy alespoň o~30~\%. Přínosem této práce je přispění do knihovny, která může v~budoucnu usnadnit některým vývojářům práci.}
\abstractEN{This bachelor thesis deals with the design, analysis, implementation and~testing of~three compression methods LZ77, LZ78 and~LZW in~the~SCT library. The~library is designed to~offer users a~wide range of~compression methods in~the~future to~facilitate the~choice of~the~right method for their project. Inside this thesis I~deal with suitable design and~description of~given algorithms. For all methods, I~managed to~reduce the tested corpuses with~appropriately selected parameters at~least by~30~\%. The benefit of~this work is contributing to a~library that can make it easier for some developers to work in the~future.}
Ladislav Zemek's avatar
Ladislav Zemek committed
\placeForDeclarationOfAuthenticity{V~Praze}
\declarationOfAuthenticityOption{4} %volba Prohlášení (číslo 1-6)
\keywordsCS{Komprese dat, dekomprese dat, Java, LZ77, LZ78, LZW, SCT}
\keywordsEN{Compression, decompression, LZ77, LZ78, LZW, SCT}
Ladislav Zemek's avatar
Ladislav Zemek committed
%\website{https://gitlab.fit.cvut.cz/zemeklad/BP} %volitelná URL práce, objeví se v tiráži - úplně odstraňte, nemáte-li URL práce

\begin{document}

%\newacronym{CVUT}{{\v C}VUT}{{\v C}esk{\' e} vysok{\' e} u{\v c}en{\' i} technick{\' e} v Praze}
%\newacronym{FIT}{FIT}{Fakulta informa{\v c}n{\' i}ch technologi{\' i}}

\begin{introduction}
	V~době, kdy se začali počítače rozšiřovat do~běžného života, vznikla potřeba ukládat čím dál větší objem dat. Z~dnešního pohledu to nevypadá jako problém, jednoduše si uložíte několika gigabytový soubor na svůj pevný disk, který má kapacitu stovky gigabytů. Dříve  ovšem měli pevné disky kapacitu jen desítky megabytů, což je téměř sto tisíckrát méně než dnes. Paměti tedy rozhodně nebylo nazbyt. Proto bylo potřeba data nějakým způsobem zmenšit a tím šetřit místo na disku. Samozřejmě za předpokladu, že nedojde ke~ztrátě informací.
Ladislav Zemek's avatar
Ladislav Zemek committed
	  
	Právě za tímto účelem vznikla bezztrátová komprese dat, která zakóduje data pomocí důmyslných algoritmů. Nová data pak mají výrazně menší velikost než ta původní.\cite{komprese} Jsou tedy vhodnější pro uložení na~disk nebo k~odeslání přes internet. Data je následně možné dekódovat pomocí dekomprese, což je inverzní operace ke kompresi. Výstupem dekomprese jsou původní data.
	
Ladislav Zemek's avatar
Ladislav Zemek committed
	Jelikož v~knihovně SCT nejsou naimplementovány zmíněné algoritmy, rozhodl jsem se je do knihovny doplnit.
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{introduction}

	\chapter{Pojmy}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~této kapitole bych rád vysvětlil a definoval některé pojmy.
\begin{itemize}
	\item \textbf{Komprese/komprimace dat} -- Zmenšování objemu dat.
	\item \textbf{Bezztrátová komprese dat} -- Komprese dat, při které nedochází ke ztrátě informace.
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Dekomprese/dekomprimace dat} -- Inverzní operace ke kompresi. Ze zmenšených dat obnoví původní.
	\item \textbf{Triplet} -- V~této bakalářské práci je tripletem myšlena jakákoliv n-tice. Je tomu tak proto, že v~knihovně SCT je každá n-tice zpracovávána pomocí třídy TripletProcessor. Neznamená to tedy vždy trojici (LZ77). V~závislosti na metodě může jít i o~dvojici (LZ78) nebo jednici (LZW).
	\item \textbf{Adaptivní kompresní metoda} -- Tyto metody si vytváří struktůry za běhu programu v~závislosti na vstupních datech. Vytvořené struktůry jsou používá ke komprimaci dat a není potřeba je ukládat společně s~daty. 
	\item \textbf{Slovníkové kompresní metody} -- Tyto metody používají pro komprimaci slovník, který v~průběhu komprimace i dekomprimace roste přidáváním nových frází. Proto patří slovníkové metody mezi adaptivní kompresní metody. Komprimovaná data poté obsahují indexy frází ve vytvářeném slovníku.
	\item \textbf{Symetrická kompresní metoda} -- Komprimace i dekomprimace těchto metod má stejnou složitost.
	\item \textbf{Asymetrická kompresní metoda} -- Komprimace i dekomprimace těchto metod má odlišnou složitost.
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Entropie} -- Míra neurčitosti systému.
	\item \textbf{Abeceda} -- Množina symbolů.
	\item \textbf{Korpus} -- Soubor dat vytvořený pro účely testování kompresních metod.
\end{itemize}

Ladislav Zemek's avatar
Ladislav Zemek committed
	
Ladislav Zemek's avatar
Ladislav Zemek committed
\chapter{Cíl práce}
Ladislav Zemek's avatar
Ladislav Zemek committed
Cílem této bakalářské práce je seznámení se s~kompresními algoritmy LZ77, LZ78, LZW a~jejich následná implementace do knihovny SCT (Small Compression Toolkit). Součástí této práce je také jejich otestování z~hlediska časové a~paměťové náročnosti, případně porovnání efektivity se stávajícími kompresními algoritmy v~knihovně. Tato knihovna by pak měla ušetřit spoustu práce programátorům, kteří díky ní nebudou muset vymýšlet vlastní implementaci těchto algoritmů.
Ladislav Zemek's avatar
Ladislav Zemek committed

\section{Existující řešení}
Ladislav Zemek's avatar
Ladislav Zemek committed
Jedná se o~poměrně zastaralé metody, které vznikli v~80.~letech 20.~století a~dnes je již v~efektivitě překonávají jiné. Nicméně metodu LZ77 je možné použít například v~programu CRUSH.\cite{crush} Z~LZ77 bylo odvozeno mnoho dalších kompresních algoritmů, které jsou velmi často používány. LZ78 byla taktéž předlohou pro jiné algoritmy například LZW nebo BTLZ. \cite{inspire} Samotná se příliš nevyužívá, protože nedosahuje tak dobrého komprimačního poměru. Naopak metoda LZW bývala poměrně hojně využívá při komprimaci monochromatických obrázků a~obecně je velmi efektivní tam, kde se opakuje velké množství dat. LZW se používala v~dřívějších verzích formátu PDF, později byla nahrazená efektivnějším algoritmem.\cite{lzwEN} Tyto algoritmy patří mezi základní kompresní metody a~jelikož je hlavním cílem SCT poskytnout co největší spektrum kompresních algoritmů, rozhodl jsem se je do knihovny doimplementovat.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Analýza požadavků}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~této části bych se rád věnoval požadavkům, které mi zadal vedoucí práce a jejich analýze. Každý softwarový projekt by měl začínat analýzou toho, co si zákazník přeje, aby program dělal nebo dodržoval. Požadavek musí být proveditelný, testovatelný a také musí byt definovaný dostatečné detailně pro účely návrhu.\cite{req} Obvykle se požadavky dělí na funkční a nefunkční.

\subsection{Funkční požadavky}
Ladislav Zemek's avatar
Ladislav Zemek committed
 Funkční požadavky objasňují, jaké má mít požadovaný software funkce, co má umět a jak se má chovat v~daných situacích. Podle zadání a diskuse s~vedoucím práce jsem určil tyto požadavky jako funkční:
 
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Dekomprese} -- Kromě výchozí funkce komprese bude možné program spustit v~režimu dekomprese.
	\item \textbf{Parametry} -- Algoritmům bude možné změnit jejich parametry, jako je například veliskost bufferů, počet prvků ve slovníku nebo jak velké části souboru se budou komprimovat najednou. Parametry bude možné měnit při spoštění přes příkazovou řádku pomocí parametrů a nebo
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Komprimace po částech} -- Algoritmy budou schopné rozdělit soubor na menší části a ty pak komprimovat. Dekomprimace bude probíhat nad celým souborem ne po častech, nicméně algoritmy budou schopny dekomprimovat i soubor, který byl předtím zakomprimován po částech. Velikost částí si určí uživatel pomocí parametru, případně využije výchozích nastavených hodnot.
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Spustitelnost} -- Program bude možné pustit se zvolenými parametry z~příkazové řadky. Nepůjde tedy pouze o~implementaci metod do knihovny, ale i o~jejich inteface, který bude možné prakticky využít.
Ladislav Zemek's avatar
Ladislav Zemek committed
	
 		
\end{itemize}		
 
\subsection{Nefunkční požadavky}
Ladislav Zemek's avatar
Ladislav Zemek committed
Nefunkční požadavky, jsou takové požadavky, které přímo neříkají, co má program umět. Jedná se převážně o~omezující nebo zpřesňující podmínky, kterými se má návrh řídit. Tyto požadavky jsem určil jako nefunkční:
Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Triplety} -- Program bude pro ukládání dat do souboru využívat již naimplementované metody třídy TripletProcesor a pro definování jednotlivých složek v~tripletu třídu TripletFieldId.
	\item \textbf{Velikostí slovníků a bufferů} -- Aby nedocházelo k~nekontrolovatelnému růstu slovníku a tím i k~zvyšování pamětové i časové náročnosti, budou mít algorytmy LZ78 a LZW parametr, který zhora omezí počet prvků ve slovníku. U~metody LZ77 bude pomocí dvou parametrů omezen vyhledávací buffer i tzv. look ahead buffer.
	\item \textbf{Velikosti prvků v~tripletu} -- Toto omezení vychází z~implementace třídy TripletProcessor, která dokáže zpracovávat pouze čísla, která je možné zapsat pomocí 28 bitů, tedy maximálně číslo 134 217 728.
	\item \textbf{Jazyk Java} -- Program bude napsán v~jave, protože celá knihovna SCT je v~tomto jazyce napsána.
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Řetězení} -- Implementace metody bude dodržovat myšlenku řetězení, tedy že různé kompresní metody bude možné za sebe zapojovat a docílit tak několikanásobné komprese.  
	
\end{itemize}

\subsection{Výjimky} ??

\chapter{Analýza algoritmů}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~této kapitole pojednávám přímo o~jednotlivých metodách. Na jakém principu fungují a v~čem se liší. Také zde určím jejich asymptotickou složitost, náročnost na pamět, výhody a nevýhody.
Ladislav Zemek's avatar
Ladislav Zemek committed
\section{LZ77}
\subsection{Popis}
Ladislav Zemek's avatar
Ladislav Zemek committed
Tento kompresní algoritmus vynalezli v~roce 1977 pánové Abraham Lempel a Jakob Ziv. Jedná se o~slovníkovou, jednoprůchodovou, adaptivní a bezztrátovou metodu. Je založena na principu klouzavého okénka (floating window), které si lze představit jako vyříznutou část dat. Při běhu programu se okénko posouvá v~datech pouze jedním směrem a to od začátku do konce. Je rozděleno na dvě části - prohlížecí okénko (search buffer) a aktuální okénko (look-ahead buffer). Jejich velikost je velmi zásadní pro efektivitu celého algoritmu. V~zásadě se dá řict, že velikost prohlížecího okénka musí být vždy větší nebo rovna velikosti aktuálního okénka, nicméně v~praxi je prohlížecí okénko obvykle tisíci násobně větší než aktuální.\cite{vohoLZ77}
\subsection{Komprese}
Ladislav Zemek's avatar
Ladislav Zemek committed
Hlavní myšlenka tohoto algoritmu spočívá v~nalenzení co nejdelšího prefixu z~nezakódovaných dat, který je zároveň obsažený i v~prohlížecím okénku. Tato metoda umožňuje menší vylepšení efektivity, pokud se při vyhledávání neomezím pouze na prohlížecí okénko, ale jen na podmínku, že prefix v~něm musí začínat. V~některých případech je možné nalezt nejdelší prefix, který přesahuje do aktualního okénka. Je tedy možné zakódovat větší část dat. Slovník této metody je tvořen všemi podřetězci, které začínají v~prohlížecím okénku.
Ladislav Zemek's avatar
Ladislav Zemek committed

Zde je jednoduchý pseudokód, který popisuje tuto metodu:

\IncMargin{1em}
Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{algorithm}[H]
Ladislav Zemek's avatar
Ladislav Zemek committed
	prohlížecí a aktualní okénko jsou prázdná\;
Ladislav Zemek's avatar
Ladislav Zemek committed
	\While{na vstupu jsou data}{
	přidej na konec aktualního okénka symbol ze vstupu\;
Ladislav Zemek's avatar
Ladislav Zemek committed
	najdi prefix $p$ z~aktualního okénka, který začíná v~prohlížecím okénku\;
	\eIf{$p$ bylo nalezeno}{
Ladislav Zemek's avatar
Ladislav Zemek committed
		zapiš trojici $(i, j, k)$, kde $i$ je vzdálenost prvního znaku nalezeného $p$ od začátku aktualního okénka, $j$ je délka $p$ a~$k$ je první nasledující symbol po $p$\;
		posun klouzavé okénko o~$j+1$\;
Ladislav Zemek's avatar
Ladislav Zemek committed
	}{
Ladislav Zemek's avatar
Ladislav Zemek committed
		zapiš triplet $(0, 0, m)$, kde $m$ je první symbol v~aktualním okénku\;
		posuň klouzavé okénko o~$1$\;
Ladislav Zemek's avatar
Ladislav Zemek committed
	}
	}
\caption{Pseudokód komprese metody LZ77}
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{algorithm}\DecMargin{1em}

\subsection{Složitost komprese}
Dle mého názoru je složitost tohoto algoritmu $O(nm)$, kde $ n $ je délka vstupních dat a $ m $ je velikost prohlížecího okénka.
Ladislav Zemek's avatar
Ladislav Zemek committed
Na první pohled není uplně zřejmé, jak jsem k~tomuto závěru došel. Rozhodl jsem se tedy své tvrzení dokázat pomocí matematického důkazu. 

Mějme asymptotickou složitost znázorněnou sumou, která vyjadřuje nejvyšší možný počet operací potřebných pro komprimaci souboru:
$$O(\sum_{i=1}^{|P|} m p_{i})$$
Ladislav Zemek's avatar
Ladislav Zemek committed
kde $P$ je množina všech prefixů nalezených v~datech, $m$ je velikost prohlížecího okénka a $p_{i}$ velikost $i$-tého prefixu z~$P$.
Ladislav Zemek's avatar
Ladislav Zemek committed

Protože součet sumy nezáleží na $ m $, mohu ho vytknout před sumu:

$$O(m\sum_{i=1}^{|P|}p_{i})$$
Ladislav Zemek's avatar
Ladislav Zemek committed
Součet sumy je vždy rovný velikosti dat, protože procházím celá vstupní data a rozděluji je na podřetězce, které najdu v~prohlížecím okénku. Nezáleží tedy na délce jednotlivých podřetězců, jejich součet velikostí bude vždy roven velikosti vstupních dat, tedy $ n $. Proto:
$$O(\sum_{i=1}^{|P|} m p_{i}) = O(mn)$$
Ladislav Zemek's avatar
Ladislav Zemek committed
Je třeba říci, že na vyhledání prefixu nebude vždy potřeba stejný počet operací, proto jsem se rozhodl použít tzv. omikron $ O~$, který složitost omezuje shora.
\subsection{Dekomprese}

Ladislav Zemek's avatar
Ladislav Zemek committed
Algoritmus je poměrně jednoduchý. Nejdříve načte ze vstupu triplet, který v~sobě obsahuje informaci o~tom, odkud a jak dlouhou část prohlížecího okénka je potřeba zapsat na výstup. Poslední část tripletu je jeden byte, který je taktéž potřeba zapsat. Následně zapsaná data rovněž přidáme na konec prohlížecího okénka. Zde je jednoduchý pseudokód:\newline
Ladislav Zemek's avatar
Ladislav Zemek committed

\begin{algorithm} [H]
	prohlížecí okénko je prázdné\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i, j, k) $, kde $i$ je vzdálenost prvního znaku hledaného podřetězce $p$ od konce prohlížecího okénka, $j$ je délka $p$ a $k$ je první nasledující symbol po $p$\;
		
		\eIf{$i$ se rovná 0}{
			zapiš $k$\;
			pridej na konec prohlížecího okénka $k$\;
		}{
			$v:=$ aktuální velikost prohlížecího okénka\; 
Ladislav Zemek's avatar
Ladislav Zemek committed
			$z:= v~- i$\;
			$p:=$ podřetězec prohlížecího okénka od indexu $z$ az do $z + j$-\;
			$p:= p + k$\;
			zapiš $p$\;
			do prohlížecího okénka přidej $p$\;
		}
	}
	\caption{Pseudokód dekomprese metody LZ77}
\end{algorithm}

\subsection{Složitost dekomprese}
Ladislav Zemek's avatar
Ladislav Zemek committed
Oproti kompresi je složitost dekomprese vcelku trivialní. Řekněme, že $ t $ je počet tripletů, pro každý triplet bude potřeba $ p_{i} $ operací, při čtení podřetězce z~prohlížecího okénka. To znamená, že celkový počet operací bude roven sumě všech délek podřetězců. Tato suma se však rovná velikosti puvodního nekomprimovaného souboru, tedy $ n $. Čímž dostáváme:  $$\sum_{i=1}^{t} p_{i} = n$$ z~čehož plyne, že složitost dekomprese je $ \Theta(n) $. 
Ladislav Zemek's avatar
Ladislav Zemek committed
Složitosti komprese i dekomprese jsou rozdílné, jedná se tedy o~asymetrickou metodu.
Ladislav Zemek's avatar
Ladislav Zemek committed
\subsection{Výhody}
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Paměť} -- Díky posuvnému okénku se v jednu chvíli zpracovává poměrně malá čast dat, proto neroste paměťová náročnost s~velikostí vstupu.  
	\item \textbf{Rychlost dekomprese} -- Při vhodně zvolené implementaci je algoritmus dekomprese lineárně závislý pouze na velikosti dat. Jeho složitost je tedy $\Theta(n)$, kde n je velikost původních dat. 
Ladislav Zemek's avatar
Ladislav Zemek committed
	
\end{itemize}
\subsection{Nevýhody}
Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Náhodná data} -- Pokud mají data příliš velkou míru entropie, algoritmus dosahuje velmi špatných výsledků, často i komprimovaný soubor zvětší. To se týká například některých formátů obrázku (jpeg).   
	\item \textbf{Rychlost komprese} -- Složitost $O(nm)$ je oproti dekompresi $ m $ krát větší. To znamená, že algoritmus bude pomalejší, protože $ m $ je obvykle velké číslo v~řádech tisíců až desetitisíců.
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{itemize}

\section{LZ78}
\subsection{Popis}
Ladislav Zemek's avatar
Ladislav Zemek committed
Metoda byla vynalezena stejnými autory jako LZ77 v~roce 1978. Z~počátku se jednalo o~velmi oblíbenou komprimační metodu až do doby než byly některé její časti patentovány.
Jedná se o~paměťově velmi náročnou metodu. Je tomu tak hlavně kvůli obvykle velkému slovníku. Metoda totiž v~původní verzi přidává do slovníku položku vždy, když zapisuje triplet, což se děje velmi často. Existuje mnoho způsobu, jak tento problém vyřešit. Například v~případě vyčerpání paměti tzv. zamrazit slovník nebo smazat a vytvořit nový. Nejznámější modifikací je LZW, kterou se také zabývám v~této práci.\cite{stringologyLZ78}

\subsection{Komprese}

Ladislav Zemek's avatar
Ladislav Zemek committed
Prvním krokem tohoto algoritmu je inicializace slovníku. Tento krok je velmi důležitý pro správný průběh dekomprese. Do slovníku se přidá slovo délky jedna za každé písmeno abecedy souboru. Abeceda je množina všech znaků, které se mohou v~souboru objevit. Obvykle se používá množina všech bytů (0~-~255), pokud komprese kóduje po bytech. Dále nastaví řetězec $ str $ jako prázdný.
Ladislav Zemek's avatar
Ladislav Zemek committed
 Následující krok se opakuje pro každý byte ze vstupu. Pokud spojení řetězce $ str $ a načtený byte  $ b $ nejsou ve slovníku, na výstup se přidá triplet obsahující index $ str $ ve slovníku a $ b $. V~opačném případě se na konec řetězce $ str $ přidá byte $ b $. Výstupem teto metody je posloupnost dvojic -- číslo, byte.
Zde je jednoduchý pseudokód, popisující kompresi:\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti byte $ b $\;
		\eIf{$str + b$ není ve $slovníku$}{
			zapiš triplet $(i, b)$, kde $i$ je index $str$ ve $slovníku$\;
			přidej na konec $slovníku$ slovo $str + b$ \;
			$str :=$ prázdný řetězec\;
		}{
			$str = str + b$\;
		}
	}
	\caption{Pseudokód komprese metody LZ78}
\end{algorithm}


\subsection{Složitost komprese}
Ladislav Zemek's avatar
Ladislav Zemek committed
Tento algoritmus velmi ovlivňuje zvolená implementace. Pokud se slovník representuje pomocí datové struktůry strom, složitost je $ \Theta(n) $, kde n je velikost vstupních dat v~bytech. Protože dokáže v~konstantní složitosti $ \Theta(1) $ říci, jestli slovník již obsahuje daný řetězec či nikoliv. Pro každy vstupní symbol vykoná $ \Theta(1) $ operací. Pro uplnost doplním jednoduchou rovnici, která ukazuje, že v~asymptotické složitosti se konstantní složitost neprojeví: $$ \Theta(1 * n ) = \Theta(n) $$
Ladislav Zemek's avatar
Ladislav Zemek committed
Jak je vidět na pseudokódu níže, algoritmus je velmi jednoduchý. Prvním a~zásadním krokem je inicializace slovníku přesně stejnou abecedou jako v~kompresi. Následně se načte triplet ze vstupu, podle něj se najde slovo ve slovníku a zapiše do souboru společně s~následujícím bytem. Tento zapsaný řetězec je přidán do slovníku na konec (přiřadí se mu nejvyšší index).\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i, b) $, kde $ i $ je index do $slovníku$ a $ b $ je následující byte\;
		$str := slovník[i]$ + $ b $\;
		zapiš $ str $\;
		přidej na konec $slovníku$ slovo $str$ \;
	}
	\caption{Pseudokód dekomprese metody LZ78}
\end{algorithm}

\subsection{Složitost dekomprese}
Ladislav Zemek's avatar
Ladislav Zemek committed
Jak bylo řečeno v~popisu, jedná se o~symetrickou metodu. Složitost je tedy stejná jako u~komprese a tedy $ \Theta(n) $, kde $ n $ je velikost původních dat. Podobně jako u~metody LZ77, máme sice menší vstup, nicméně je potřeba přičíst i~režiji slovníku na zrekonstruováná slova. Jedná se o~strom, to znamená, že metoda pro rekonstrukci slova udělá počet operací rovný délce slova. Pokud tedy všechny délky posčítáme, dostaneme $ n $.

\subsection{Výhody}
\begin{itemize}  
	\item \textbf{Rychlost komprese i dekomprese} -- Při vhodně zvolené implementaci je algoritmus komprese i dekomprese lineárně závislý pouze na velikosti dat. Jejich složitost je $\Theta(n)$, kde n je velikost původních dat.
	\item \textbf{Implementace} -- Metoda je poměrně jednoduchá na implementaci, především pak dekomprese.
	\item \textbf{Velikost tripletů} -- Do souboru se ukládají pouze dvojice -- číslo, byte.
\end{itemize}
\subsection{Nevýhody}
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Paměť} -- Paměťová náročnost roste s~velikostí vstupu. Proto se při implementaci musí počítat s~tím, že metoda vyčerpá přidělenou pamět a~podle toho se zachovat.
	\item \textbf{Náhodná data} -- Podobně jako u~metody LZ77, při velké entropii dat dochází k~horší kompresi, někdy muže dojít až k~zvětšení souboru.
Ladislav Zemek's avatar
Ladislav Zemek committed
Tuto slovníkovou, symetrickou, adaptivní a jednoprůchodovou metodu vytvořili Abraham Lempel, Jacob Ziv a Terry Welch v~roce 1984. Jedná se o~modifikaci metody LZ78. Hlavním rozdílem je, že algoritmu pro dekomprimaci dat stačí pouze indexy ve slovníku. Již tedy nepotřebuje následující byte jako jeho předchůdce. To má ale také za následek rychlejší růst počtu frází ve slovníku. Slovník tedy typicky bývá větší než u~LZ78 a spotřebovává rychleji paměť. Je tedy potřeba s~tím počítat při implementaci a tento problém vhodně vyřešit. Obecně platí, že čím větší slovník, tím lepší kompresní poměr. Algoritmus je navržen tak aby byl rychlý, nicméně ne vždy je optimální, protože neprovádí žádnou analýzu dat během komprese.\cite{stringologyLZW}
Ladislav Zemek's avatar
Ladislav Zemek committed
Metoda se stala velmi oblíbenou kolem roku 1987, protože poskytovala nejlepší kompresní poměr. Byla využita například ve formátu GIF, později například ve formátu PDF nebo v~unixovém nástroji $ compress $. Stala se první široce využívanou kompresní metodou na počítačích.
Ladislav Zemek's avatar
Ladislav Zemek committed
První krok algoritmu je inicializace, která probíhá totožně jako v~metodě LZ78. Následně se načte byte $ b $ ze vstupu. Pokud je řetězec $ str + b $ ve slovníku, nastaví se jako $ str $. Pokud není, do souboru se zapíše triplet $ (i) $, kde $ i $ je index fráze $ str $. Do slovníku se pak přidá fráze $ str + b $ a jako nové $ str $ se nastaví $ b $. To je hlaní změna oproti metodě LZ78, která umožnuje právě vynechání nasledujícího bytu v~tripletu. Tento postup se opakuje dokud jsou na vstupu data.\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti byte $ b $\;
		\eIf{slovo $str + b$ není ve $slovníku$}{
			zapiš triplet $(i)$, kde $i$ je index $str$ ve $slovníku$\;
			přidej na konec $slovníku$ slovo $str + b$ \;
			$str := b$ \;
		}{
			$str = str + b$\;
		}
	}
	\caption{Pseudokód komprese metody LZW}
Ladislav Zemek's avatar
Ladislav Zemek committed
\subsection{Složitost komprese}
Metoda obsahuje pouze drobné změny oproti LZ78, které nemají vliv na asymptotickou složitost. Je totožná se složitostí komprese LZ78, tedy $\Theta(n)$. Proto si dovolím vynechat její odvození.

\subsection{Dekomprese}

Ladislav Zemek's avatar
Ladislav Zemek committed
Princip dekomprese není na první pohle pochopitelný, protože nepřidávám do slovníku slovo tvořené právě zapisovaným řetězcem, ale tím který byl zapsaný v~minulém běhu a pouze prvním znakem zapisovaného řetězce. Tato skutečnost je dána tím, že komprimace přidává do slovníku slovo délky $ n $, končící právě načteným bytem, ale do souboru zapisuje index předchozího slova délky $ n - 1  $, tedy bez načteného bytu. Dekomprimační algoritmus pak vždy ví, že do slovníku je potřeba přidat slovo z~minulého běhu zakončené prvním znakem z~fráze nalezené ve slovníku. 
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
Specialní případ nastane, pokud algoritmus ze vstupu načte triplet s~indexem, který není ve slovníku. Toto číslo však může být pouze o~jedna větší než nejvyšší obsazený index, jinak se jedná o~chybu kompresního algoritmu. V~tomto případě do slovníku vloží slovo, které bylo vloženo v~předchozím kroku zakončené jeho prvním znakem.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
K~tomuto případu dochází, pokud bylo pro komprimaci použito slovo na posledním indexu (naposledy přidaná fráze do slovníku).
Ladislav Zemek's avatar
Ladislav Zemek committed

Zde je pseudokód, popisující tento algoritmus:\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i) $\;
Ladislav Zemek's avatar
Ladislav Zemek committed
		\eIf{$slovník$ obsahuje slovo s~indexem $ i $}{
Ladislav Zemek's avatar
Ladislav Zemek committed
			$ fráze :=  slovník[i]$\;
			zapiš $fráze$\;
			přidej na konec $slovníku$ slovo $str $ + první znak $ fráze $ \;
		}{
			$fráze = str $ + první znak ze $str$\;
			zapiš $str$\;
			přidej na konec $slovníku$ slovo $fráze$\; 
		}
	$str := fráze$
	}
	\caption{Pseudokód komprese metody LZW}
\end{algorithm}

\subsection{Složitost dekomprese}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~závislosti na implementaci se muže složitost tohoto algoritmu lišit. Nicméně pokud struktůru slovníku representuje strom je možné dosáhnout složitosti $ \Theta(n) $, kde $ n $ je velikost původních dat před kompresí. Metoda LZW je navíc symetrická, takže složitost je stejná jako u~komprese. Důvod proč jsem došel k~této složitosti je analogický s~určením složitosti dekomprese u~LZ78. Nejvice kroků bude potreba udělat tam, kde se ze slovníku rekonstruuje řetězec, což odpovídá celkové velikosti dat. Ostatní operace mají zanedbatelnou režii a při určování asymptotické složitosti se zanedbávají. 
Ladislav Zemek's avatar
Ladislav Zemek committed

\subsection{Výhody}
\begin{itemize}  
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Rychlost komprese i dekomprese} -- Stejně jako u~LZ78 je hlavní výhodou rychlost. Při správné implementaci $ \Theta(n) $.
	\item \textbf{Velikost tripletů} -- Do souboru se ukládají pouze číslo. Na jeden triplet je tedy potřeba uložit nejmeně dat z~metod kterými se zabývá tato práce.
	\item \textbf{Kompresní poměr} -- Na běžném anglickém textu dosahuje metoda velmi dobrých výsledků, typicky zmenší soubor zhruba o~50 %.
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{itemize}
\subsection{Nevýhody}
\begin{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{Paměť} -- Paměťová náročnost roste s~velikostí vstupu. Roste dokonce rychleji než u~LZ78. Proto se při implementaci musí počítat s~tím, že metoda vyčerpá přidělenou pamět.
	\item \textbf{Náhodná data} -- Podobně jako u~předchozích metod, dochází při velké entropii dat k~horší kompresi. Může dojít až k~zvětšení souboru.
Ladislav Zemek's avatar
Ladislav Zemek committed
	
	
\end{itemize}

\section{Závěr analýzy}
Ladislav Zemek's avatar
Ladislav Zemek committed
Hlavním přínosem těchto metod je samotný princip na jakém fungují. Především pak LZ77 z~které vznikla řada algoritmů, které se dnes využívají v~praxi. Největší nevýhodou těchto algoritmů je špatný kompresní poměr na datech, ve kterých se neopakují delší řetězce znaků. Další nevýhodou LZ78 a LZW je velká náročnost na paměť. Jejich kompresní poměr je obvykle závislí na velikosti slovníku. Nicméně i přes tyto nevýhody se LZW v~některých programech používá dodnes, protože je velmi rychlá a vhodná na klasický text.
Ladislav Zemek's avatar
Ladislav Zemek committed


\chapter{Implementace}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~této kapitole popisuji realizaci metod přímo v~knihovně SCT. Zásadní věcí v~implementaci těchto metod je reprezentace slovníků, ať už u~komprese nebo dekomprese. Efektivní přidávání a hledání ve slovníku je zásadní pro efektivitu techto metod. Vysvětluji jaké parametry bude moci nastavit uživatel a~jak ovlivní efektivitu nebo paměťovou náročnost. Jakým způsobem metody načítají data ze vstupu a~jak zapisují triplety do souboru.  
Ladislav Zemek's avatar
Ladislav Zemek committed

\section{Řetězení}
Ladislav Zemek's avatar
Ladislav Zemek committed
V~knihovně se používá princip řetězení (chaining). To znamená, že některé metody se dají zapojovat za sebe. Zde je ukázka kódu:
Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{lstlisting}
ChainBuilder.create(io::openParse)
.chain(lz77::compress)
.chain(t2b)
.end(bytes -> io.saveObject(bytes, f2.toPath()))
.accept(f1.toPath());
\end{lstlisting}

Význam kódu není zatím relevantní, nicméně představuje použití komprese. Důležitý je princip, podle kterého je naimplementovaná metoda \classname{compress}. Hlavní podmínkou pro chainování jsou dva parametry metody. První je vstupní parametr a druhý výstupní. Respektive druhým parametrem je objekt consumer, který má za parametr datový typ výstupu. Ukázka kódu:
\begin{lstlisting}
public void compress(ByteBuffer byteBuffer,
Consumer<TripletSupplier> tripletSupplierConsumer) {...}
\end{lstlisting}

\newpage

Další možností je implementovat celý objekt tak, aby mohl být řetězený, pomocí implementace rozhraní \classname{Chainable<x, y>}, kde x je datový typ vstupního parametru a y datový typ pro výstup. Zde je hlavička třídy, kterou je možné řetězit:

\begin{lstlisting}
public abstract class TripletToByteConverter<T> 
   implements Chainable<TripletSupplier, List<byte[]>>
	{...}
\end{lstlisting}

Ladislav Zemek's avatar
Ladislav Zemek committed
Zde je vstupní parametr typu \classname{ByteBuffer}, to znamená že předchozí metoda v~řetězci musela mít druhý parametr typu \classname{Consumer<ByteBuffer>}. Vystupním parametrem je \classname{TripletSuplier}, následující metoda tedy musí mít první parametr \classname{TripletSuplier}. V~tomto připadě je to objekt \classname{t2b}, který je instancí třídy \classname{TripletToByteConverter}, která implementuje rozhraní \newline \classname{Chainable<TripletSupplier, List<byte[]>>}.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
Uvnitř metody se pak volá třídní metoda consumera \classname{accept}, která posílá data do další metody. Objekt musí mít vždy implementovanou metodu \classname{setConsumer}, která nastavý consumera. Ten funguje stejne jako u~metody.
Ladislav Zemek's avatar
Ladislav Zemek committed

\section{Načítání a zápis do souboru}
Ladislav Zemek's avatar
Ladislav Zemek committed
Načítání ze souboru se u~komprese a dekomprese liší. Zatím co v~prvním případě, načítám ze souboru  byty u~dekomprese se jedná o~načtení celého objektu ze souboru. Je tomu tak proto, že po kompresi se ukládá celé pole bytů jako objekt, aby bylo možné správně načítat uložené triplety. Dekomprese ukládá data do souboru po bytech, aby byl soubor stejný jako před komprimací.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
O~vše se stará třída \classname{FileIO}, která má jako parametr velikost, podle které rozděluje načítaný soubor na menší části a ty se následně zkomprimují a v~podobě tripletů uloží do souboru. Zkomprimované soubory se načítají i dekomprimují jako celek, aby nedocházelo k~narušení slovníků. Mohlo by se stát, že načteme menší nebo větší část tripletů než jaká byla vytvořena z~jedné části pri kompresi. Takový případ by pak vedl k~sestavení špatného slovníku, což by vedlo k~chybám v~dekompresi.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Triplety}

Ladislav Zemek's avatar
Ladislav Zemek committed
Nedílnou součástí všech implementovaných kompresních metod jsou triplety. Jejich spracování a následný zápis je sprostředkován pomocí rozhraní\newline \classname{TripletSupplier}, které se vždy používá jen pro vytvoření anonymní vnitřní trídy, která implementuje metodu visit, jejímž parametrem je \classname{TripletProcessor}. Ten dokáže pomocí metody \classname{write} zapsat jednotlivé části tripletu do souboru.\newpage Zde je ukázka kódu:

Ladislav Zemek's avatar
Ladislav Zemek committed


\begin{lstlisting}
output.accept((TripletSupplier) new TripletSupplier() {
	@Override
	public void visit(TripletProcessor visitor) {
	visitor.write(nodeFieldId, num);
	visitor.write(characterFieldId, b);
	}
});
\end{lstlisting}
Ladislav Zemek's avatar
Ladislav Zemek committed
Metoda \classname{write} má dva parametry. Druhým parametrem je je hodnota zapisované složky tripletu. Prvním je objekt reprezentovaný třídou \classname{TripletFieldId}, který popisuje, kolik bitů má zapisovaná hodnota. \classname{TripletProcessor} se postará o~správný zápis bitů. To je velká výhoda, protože u~všech metod vždy dopředu vím, jaké největší číslo může být zapsáno.
Ladislav Zemek's avatar
Ladislav Zemek committed

Ladislav Zemek's avatar
Ladislav Zemek committed
 Například, pokud metoda pracuje se slovníkem, jehož maximálně počet frází je $ 2^{10}$. Není potřeba zapisovat indexy jako integer, který má 32 bitů. Pro zapsání jakéhokoliv indexu je potřeba nejvýše 11 bitů, zbytek jsou redundantní data. Tento postup v~tomto případě ušetří na každém tripletu 22 bitů, což je velká úspora paměti, která výrazně zlepší kompresní poměr. Samotná implementace třídy \classname{TripletFieldId} umožnuje zapsat pouze číslo o~velikosti 28 bitů. Tato skutečnost ale není omezující, protože slovník s~takovým počtem frází ($ 2^{29} $ a více) by byl velmi neefektivní.
Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Parametry}
Ladislav Zemek's avatar
Ladislav Zemek committed
Každá metoda má třídu s~názvem \classname{<jméno metody>ProviderParametrs.java}. Tato třída reprezentuje všechny nastavitelné parametry, které ovlivňují kompresi i dekompresi. Metodě se předává jako jediný parametr v~konstruktoru. Tyto parametry jsou podrobně popsány dále v~realizaci metod.
Ladislav Zemek's avatar
Ladislav Zemek committed

\section{Spustitelný klient}
Ladislav Zemek's avatar
Ladislav Zemek committed
Každá metoda má třídu s~názvem \classname{<jméno metody>Client.java}. Tato třída obsahuje funkci $main$, to znamená, že je možné jí spustit. Jedná se o~spustitelný program z~příkazové řádky, který komprimuje popřípadě dekomprimuje soubor podle zvoleného přepínače a parametrů. Jednotlivé klienty a jejich použití popisuji pro každou metodu zvlášť v~kapitolách realizace. Klient je zatím spustitelný příkazem: \newline
 java -jar target/sct-RELEASE.jar <jméno vstupního souboru>\newline	<jméno vytvořeného souboru> <případné přepínače>\newline Při změně klienta je potřeba v~souboru pom.xml změnit cestu k~správné $main$ funkci, kterou chcete spustit a následně zkompilovat projekt.\newpage
Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Realizace LZ77}
Ladislav Zemek's avatar
Ladislav Zemek committed
\subsection{Struktůra adresáře}

Ladislav Zemek's avatar
Ladislav Zemek committed
Na schématu adresářové struktůry můžete vidět třídu LZ77, která inicializuje kodér \classname{LZ77TripletCoder}. Ten se stará o~kompresi i dekompresi a zápis tripletů. Podrobnější popis tříd a jejich funkcí naleznete dále v~textu.
Ladislav Zemek's avatar
Ladislav Zemek committed

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz77.
		.2 LZ77.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZ77Client.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZ77ProviderParameters.java\DTcomment{třída obsahující všechny parametry}.
Ladislav Zemek's avatar
Ladislav Zemek committed
		.2 LZ77Test.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
Ladislav Zemek's avatar
Ladislav Zemek committed
		.3 LZ77TripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i~dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	\renewcommand\figurename{Struktura}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\caption{adresářová struktura metody LZ77}
	\label{dirtree}
\end{figure}

\subsection{Parametry}

Pro metodu LZ77 jsou důležité tři parametry. Velikost prohlížecího okna, aktualního okna a počet bytů, který udává po jak velkých částech se bude soubor komprimovat. 
Ladislav Zemek's avatar
Ladislav Zemek committed
Velikost prohlížecího i aktualního okna se zadává jako exponent $ e $. Následně se pak velikost oken vypočte jako $ 2^{e-1} $, $ e $ je pak nejvyšší počet bitů, potřebný pro zapsání jakehokoliv indexů do tripletu. Velikost třetího parametru se pak pohybuje obvykle od statisíců do desítek milionů (0,1~-~10~mb), nicméně není vyloučeno použít i jiné hodnoty. Na jak velké části soubor rozdělíte, ovlivní výslednou komprimaci. Jako parametr prohlížecího okna se obvykle volí čísla od 11 do 16, tj. velikost okna 1024 -- 32768 znaků. Aktualní okno je typicky mnohem menší, obvykle 64 -- 256 znaků, tomu odpovídá parametr od 7 do 9. 
Ladislav Zemek's avatar
Ladislav Zemek committed

\subsection{Hlavička souboru}

Ladislav Zemek's avatar
Ladislav Zemek committed
Na začátku komprese se uloží jako první specialní triplet, který v~této práci označuji jako hlavička souboru. V~případě LZ77 obsahuje informaci o~tom jaký byl exponent velikosti prohlížecího a aktualního okna. Tato informace je důležitá pro načítání dat z~tripletů. Udává kolik bitů je potreba prečíst abychom získali uložené číslo. Bez této hlavičky by nebylo možné soubor dekomprimovat.
Ladislav Zemek's avatar
Ladislav Zemek committed


 
\subsection{Implementace komprese}

Ladislav Zemek's avatar
Ladislav Zemek committed
Ve třídě \classname{LZ77} je implementována metoda \classname{compress}, která zajišťuje správný průběh komprese. Vstupním parametrem je \classname{byteBuffer}, což je instance třídy \classname{ByteBuffer}. Pokud je vstupní parametr null, je to signál, že je komprese u~konce. Na úplném začátku algoritmu se do souboru zapíše hlavička.
Ladislav Zemek's avatar
Ladislav Zemek committed
Pomocí \classname{byteBufferu} a parametrů se inicializuje instance třídy \linebreak \classname{LZ77TripletCoder}, která se stará o~samotnou kompresi a zápis tripletů. Komprese souboru se volá pomocí metody \classname{encode}, ta má jako parametr konsumera, do kterého se zapisují triplety.
Ladislav Zemek's avatar
Ladislav Zemek committed
Většina předchozího textu byla jen popis použití nástrojů knihovny, který je u~všech metod podobný, proto ho dále nebudu popisovat. Z~hlediska implementace je nejduležitější metoda \classname{encode}, která zajištuje celý algoritmus komprese LZ77. 
Ladislav Zemek's avatar
Ladislav Zemek committed
Protože načítání vstupů funguje tak, že program načte celou část souboru po bytech do pole, rozhodl jsem se toho využít při implementaci. Místo pole do kterého bych byty přidával a odebíral tak aby vždy odpovídali rozměrům prohlížecího a aktualního okénka, jsem se rozhodl, použít pouze indexy v~poli bytů.
Ladislav Zemek's avatar
Ladislav Zemek committed
První index ukazuje, kde v~poli začíná prohlížecí okno. Druhý, kde je začátek aktualního a tedy i konec prohlížecího okna a třetí, kde je konec aktualního okna. Těmito indexy si tedy ohraničím klouzavé okénko. To se pak velmi snadno posouvá daty. Vždy jen ke všem indexům přičtu velikost zakódovaného řetězce.
Ladislav Zemek's avatar
Ladislav Zemek committed
Na počátku algorimu jsou první dva indexy rovny nule, druhý se pak s~prvním komprimovaným řetězcem začne zvětšovat. Index který ukazuje na začátek se začne měnit až když prohlížecí okénko odpovídá požadované velikosti. Poslední index, který ukazuje na konec aktualního okénka, je na začátku rovný požadované velikosti, v~pruběhu komprimace se zvětšuje dokud nenarazí na konec pole.
Ladislav Zemek's avatar
Ladislav Zemek committed
Samotné hledání prefixu z~aktualního okénka probíhá tak, že vezmu první byte v~aktualním okénku a pokusím se ho najít v~prohlížecím okénku. Pokud je nalezena shoda, vezmu následující byte z~aktualního okénka a porovnám ho s~následujícím bytem v~prohlížecím okénku. Pokud jsou schodné, postup opakuji, pokud ne, zapamatuji si toto slovo jako kandidáta na nejdelší prefix. A~celý postup opakuji dokud neprojdu celé prohlížecí okno a nenjdu největší možný prefix. Prohlížecí okno se postupně zmenšuje díky skutečnosti, že v~poli před kandidátem na nejdelší slovo žádné delší nemůže být, protože vím, že nejdelší nalezený prefix v~předchozí časti je právě kandidát. Aktualní okénko je stejné, jako na začátku, mění se až po nalezení nejdelšího prefixu.\pagebreak
Ladislav Zemek's avatar
Ladislav Zemek committed
Po nalezení nejelšího prefixu se všechny indexy posunou o~jeho velikost zvětšenou o~1 a do konsumera se uloží triplet, který je tvořen třemi složkami: $$ (i, j, b) $$ Kde $i$ je vzdálenost začátku slova od počátku aktualního okénka. Na zapsání je potřeba počet bitů, který udává první parametr zadaný uživatelem. Další složkou je číslo $j$, což je délka prefixu. Počet bitů pro zápis čísla $j$ udává druhý parametr. Poslední složkou je byte $b$, následující po nalezeném prefixu v~aktualním okně. Ten má vždy 8 bitů.
Ladislav Zemek's avatar
Ladislav Zemek committed

Po kompresi se všechny triplety převedou pomocí třídy \linebreak \classname{TripletToByteConverter} na byty a následně zapíšou do souboru.

\subsection{Implementace dekomprese}

Uvnitř třídy \classname{LZ77TripletCoder} je naimplementovaná metoda \classname{decode}, která má jako vstupní parametr input třídy \classname{TripletProcessor}, což je výsledek zpracování vstupu třídou \classname{ByteToTripletConverter}, která převádí vstupní byty na triplety.

Ladislav Zemek's avatar
Ladislav Zemek committed
Implementace dekomprese je velmi jednoduchá. Na začátku si inicializuji proměnou builder třídy  ByteBuilder, čož je třída obalující pole bytů a poskytující některé vhodné metody pro práci s~polem, především pak metodu appent, která přidá byte na konec pole.
Ladislav Zemek's avatar
Ladislav Zemek committed
Následující krok se opakuje dokud jsou v~proměnné $input$ triplety. Načtu triplet $ (i, j, b) $. Pokud jsou první i druhá složka rovny nule, pak na konec $builderu$ přidám byte $b$ z~třetí složky tripletu, jinak najdu byte vzdálený $i$ bytů od konce $builderu$. Od tohoto bytu přidám $j$ znaků na konec $builderu$. Sekvence zapidaných bytů odpovídá zakódovanému prefix. Na konec $builderu$ přidám byte $b$ z~tripletu.
Ladislav Zemek's avatar
Ladislav Zemek committed
Pokud dojdou triplety, $input$ vratí číslo $ -1 $ v~následující načtené složce tripletu. 
Ladislav Zemek's avatar
Ladislav Zemek committed
V~případě této metody není problém dekódovat i triplety, které vznikli z~jiné části při kompresi. Díky skutečnosti, že při dekódování tripletu mám k~dispozici vždy celé pole předchozích bytů, vždy správně najdu index od kterého kopírovat data. To je také duvod, proč nemusí být v~hlavičce velikost částí na rozdíl od dalších dvou metod.
Ladislav Zemek's avatar
Ladislav Zemek committed
Po dekomprimaci se pole bytů zapíše do souboru. Nový soubor je totožný s~tím před komprimací.\pagebreak
Ladislav Zemek's avatar
Ladislav Zemek committed


\subsection{Spustitelný klient}

Ladislav Zemek's avatar
Ladislav Zemek committed
Klient metody LZ77 může být spuštěn s~těmito přepínači:
Ladislav Zemek's avatar
Ladislav Zemek committed

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s~tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-sb <$e$>} -- tento přepínač říká, jak velké prohlížecí okénko bude použito ($ 2^{e-1} $)
	\item \textbf{-lb <$e$>} -- tento přepínač říká, jak velké aktualní okénko bude použito ($ 2^{e-1} $)
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-p <$n$>} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v~bytech)
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{itemize}
Ladislav Zemek's avatar
Ladislav Zemek committed
Poznámka: Pokud  při kompresi některý z~posledních tří přepínačů chybí nebo je v~některém chyba, metoda se zavolá s~výchozími parametry, které jsou: -sb~$14 $ -lb $8 $ -p $ 1 000 000 $.\newpage
Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Realizace LZ78}

\subsection{Struktůra adresáře}

Ladislav Zemek's avatar
Ladislav Zemek committed
Na schématu adresářové struktůry můžete vidět třídu LZ78, která inicializuje kodér \classname{LZ78TripletCoder}. Ten se stará o~kompresi i dekompresi a zápis tripletů. Podrobnější popis tříd a jejich funkcí naleznete dále v~textu.

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz78.
		.2 dictionary.
		.3 DecodeDictionary.java.\DTcomment{interface pro dekompresi}.
		.3 DecodeList.java.\DTcomment{slovník dekomprese pomocí seznamu}.
		.3 DecodeTree.java.\DTcomment{slovník dekomprese pomocí stromu}.
		.3 DecodeNode.java.\DTcomment{třída reprezentující frázi/uzel pro dekomprese}.
		.3 EncodeDictionary.java.\DTcomment{slovník komprese}.
		.3 EncodeNode.java.\DTcomment{třída reprezentující uzel komprese}.
		.2 LZ78.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZ78Client.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZ78ProviderParameters.java\DTcomment{třída obsahující všechny}.
		.2 LZ78Test.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
Ladislav Zemek's avatar
Ladislav Zemek committed
		.3 LZ78TripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i~dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	}
	\renewcommand\figurename{Struktura}
	\caption{adresářová struktura metody LZ78}
	\label{dirtree}
\end{figure}

\subsection{Parametry}

Ladislav Zemek's avatar
Ladislav Zemek committed
Metoda má pouze dva parametry. Prvním je velikost slovníku, tedy počet frází, které je možné uložit. Druhým parametrem je velikos částí (v~bytech). Jeho význam je stejný jako u~metody LZ77. Podobně jako u~LZ77 se první parametr zadává pomocí exponentu $e$. Velikost slovníku se pak vypočte jako $2^{e-1}$, $e$ je počet bitů potřebný pro zapsání jakehokoliv indexu ze slovníku. Obvykle se $ e $ pohybuje kolem hodnot 12-19, což odpovídá 2048 -- 262 144 frází ve slovníku. 
\subsection{Hlavička souboru}

Ladislav Zemek's avatar
Ladislav Zemek committed
Prvním udajem v~hlavičce je parametr $ e $, který udává kolik bitů má číslo indexu v~tripletu. Druhým údajem je velikost částí. Důležitost druhého parametru vysvětlím v~části o~implementaci dekomprese.
\subsection{Implementace komprese}

Kompresi zajišťuje metoda \classname{encode} uvnitř třídy \classname{LZ78TripletCoder}.

Ladislav Zemek's avatar
Ladislav Zemek committed
Jako struktůru pro slovník jsem se rozhodl použít strom, jehož uzly jsou reprezentováni pomocí třídy \classname{EncodeNode}. Strom je implementovaný tak, že každý uzel má v~sobě mapu potomků. To umožňuje velmi rychlé vyhledávání frází.

Slovník reprezentuje metoda \classname{EncodeDictionary}, \classname{compress} vždy načte byte a předá ho slovníku, aby ho zpracoval pomocí metody \classname{processByte}.

Ladislav Zemek's avatar
Ladislav Zemek committed
Slovník si drží ukazatel na kořen stromu a na aktualní uzel. Kořen sám funguje jako prázdný znak, který má v~mapě potomků všech 256 bytů. Jedná se o~inicializaci slovníku, která je popsaná v~kapitole o~analýze algoritmu LZ78 a probíhá v~konstruktoru slovníku. 
Ladislav Zemek's avatar
Ladislav Zemek committed
Na začátku je ukazatel na aktualní uzel rovný ukazateli  kořenu. Vždy když se zavolá metoda \classname{processByte}, zkontroluje se zda má aktualní uzel v~mapě potomků uzel na hodnotě načteného bytu. Pokud ano, ukazatel na aktualní uzel se posune do potomka. Pokud ne, zapíše se triplet $$ (i, b)  $$ kde $i$ je číslo aktualního uzlu a $b$ je načtený byte. Aktualnímu uzlu se přidá potomek do mapy na hodnotu $b$. Ukazatel na aktualní uzel se posune do kořene. 

Tímto způsobem se ve slovníku zjišťuje, jestli obsahuje hledanou frázi. Pokud slovník zjistí, že ji neobsahuje, přidá jí.

Ladislav Zemek's avatar
Ladislav Zemek committed
V~průběhu komprese se kontroluje kolik frází je již ve slovníku. Pokud počet frází dosáhne maxima, které udává parametr, slovník se zamrazí. Komprese pokračuje dál, nicméně slovník se už nezvětšuje.
\subsection{Implementace dekomprese}

Kompresi zajišťuje metoda \classname{decode} uvnitř třídy \classname{LZ78TripletCoder}.

Ladislav Zemek's avatar
Ladislav Zemek committed
Pro účely testování jsem naimplementoval dva slovníky pro dekompresi. Liší se především tím, s~jakou složitostí přidávají a získávají ze slovníku fráze.
Ladislav Zemek's avatar
Ladislav Zemek committed
Na uplném začátku se načte hlavička a inicializuje slovník jako u~komprese. Dále se načte triplet ze souboru. Jeho složky jsou index $ i $ a následující byte $ b $. Index určuje kde se nachází fráze ve slovníku. Zde se implementace liší. 

Třída \classname{DecodeList} representuje slovník jako pole frází. Pomocí indexu najdu frázi a tu poté zapíšu. 

Ladislav Zemek's avatar
Ladislav Zemek committed
Třída \classname{DecodeTree} si slovník udržuje jak strom, podobně jako při kompresi. Rozdíl je v~tom, že všechny uzly jsou uloženy v~poli ve stejném pořadí v~jakém byly přidány, aby bylo možné hledat uzel na indexu $ i $ v~konstatní složitosti. Fráze se pak zrekonstruuje rekurzivně pomocí odkazů na rodiče, kterého každý uzel má. Kořen funguje jako zarážka rekurze, protože jeho rodič je null. Ukončovací podmínka tedy zní: Pokud je rodič null, ukonči rekurzi.
Ladislav Zemek's avatar
Ladislav Zemek committed
Poté co je fráze získaná ze slovníku zapíše se na konec výstupního souboru společně s~následujícím bytem $ b $. Následně je na konec slovníku přidána fráze složená ze zapsaného řetězce a bytu $ b $.
Ladislav Zemek's avatar
Ladislav Zemek committed
V~přidávání frází do slovníku se tyto implementace také liší. Zatímco třída \classname{DecodeList} vytvoří novou frázi tak, že zkopíruje řetězec a přidá k~němu byte $ b $, třída \classname{DecodeTree} přidá uzlu na indexu $ i $ pouze potomka do mapy s~klíčem hodnoty $ b $.
Ladislav Zemek's avatar
Ladislav Zemek committed
Třída \classname{DecodeList} má tedy složitější přidávání frází, ale jednodušší získávání. U~třídy \classname{DecodeTree} je tomu přesně na opak. Nicméně testování ukázalo, že v~rychlosti komprese se neprojeví žádné velké rozdíly.
Ladislav Zemek's avatar
Ladislav Zemek committed
V~průběhu dekomprese, algoritmus zaznamenává kolik dat bylo dekódováno. Pokud počet dekódovaných dat dosáhne velikosti druhého parametru z~hlavičky, stávající slovník se vymění za nově vytvořený. Nový slovník je vztvořen pomocí konstruktoru a uplně prázdný. Tvorba slovníku začne od znovu. Tímto zpusobem se správně dekódují jednotlivé části souboru. 
\subsection{Spustitelný klient}

Ladislav Zemek's avatar
Ladislav Zemek committed
Klient metody LZ78 může být spuštěn s~těmito přepínači:

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s~tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-nc <$e$>} -- tento přepínač říká, kolik maximálně frází muže být ve slovníku ($ 2^{e-1} $)
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-p <$n$>} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v~bytech)
Ladislav Zemek's avatar
Ladislav Zemek committed
Poznámka: Pokud  při kompresi některý z~posledních dvou přepínačů chybí nebo je v~některém chyba, metoda se zavolá s~výchozími parametry, které jsou: -nc  $17 $ -p $ 1 000 000 $.\newpage
Ladislav Zemek's avatar
Ladislav Zemek committed
\section{Realizace LZW}
Ladislav Zemek's avatar
Ladislav Zemek committed

\subsection{Struktůra adresáře}

Ladislav Zemek's avatar
Ladislav Zemek committed
Na schématu adresářové struktůry můžete vidět třídu LZW, která inicializuje kodér \classname{LZWTripletCoder}. Ten se stará o~kompresi i dekompresi a zápis tripletů. Metoda LZW využívá pro reprezentaci uzlů ve slovníku stejné třídy jako LZ78. Podrobnější popis tříd a jejich funkcí naleznete dále v~textu.

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz78.
		.2 dictionary.
		.3 DecodeNode.\DTcomment{třída reprezentující frázi/uzel pro dekomprese}.
		.3 EncodeNode.\DTcomment{třída reprezentující frázi/uzel pro kompresi}.
		.1 lzW.
		.2 dictionary.
		.3 DecodeDictionary.java.\DTcomment{slovník dekomprese}.
		.3 EncodeDictionary.java.\DTcomment{slovník komprese}.
		.2 LZW.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZWClient.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZWProviderParameters.java\DTcomment{třída obsahující všechny parametry}.
		.2 LZWTest.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
Ladislav Zemek's avatar
Ladislav Zemek committed
		.3 LZWTripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i~dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	}
	\renewcommand\figurename{Struktura}
	\caption{adresářová struktura metody LZW}
	\label{dirtree}
\end{figure}

\subsection{Parametry a hlavička souboru}
Metoda má totožnou hlavičku i nastavitelné parametry jako LZ78. 

\subsection{Implementace komprese}

Kompresi zajišťuje metoda \classname{encode} uvnitř třídy \classname{LZWTripletCoder}.

Ladislav Zemek's avatar
Ladislav Zemek committed
Od imlementace komprese LZ78 se tato implementace liší pouze v~chování při zápisu tripletu, který má pouze jedinou složku: $$ (i) $$ Zapisuje totiž pouze index ve slovníku. Poté přidá nového potomka aktualnímu uzlu, s~hodnotou posledního načteného bytu. Následně neposune ukazatel do kořene stromu, jako je tomu u~LZ78, ale do potomka kořene, který má hodnotu jako zpracovávaný byte.
\subsection{Implementace dekomprese}
Dempresi zajišťuje metoda \classname{decode} uvnitř třídy \classname{LZWTripletCoder}.

Ladislav Zemek's avatar
Ladislav Zemek committed
Třída \classname{DecodeDictionary} ve složce \classname{lzw} representuje slovník, který používá struktůru stromu. Jeho uzly jsou uloženy v~poli, aby bylo možné hledat index fráze v~konstantní složitosti. Fráze se pak sestavý pomocí rekurzivního procházení přes rodiče až do kořene. 
Ladislav Zemek's avatar
Ladislav Zemek committed
Hlavním rozdíl proti dekompresi LZ78 je, že načtený index z~tripletu nemusí být ve slovníku. V~takovém případě je dekódovaný řetězec stejný jako poslední přidaná fráze. Do stromu se přidá uzel s~hodnotou, která odpovídá prvnímu znaku naposledy přidané fráze. Tento uzel je potomkem naposledy použitého uzlu.
Ladislav Zemek's avatar
Ladislav Zemek committed
Pokud index ve slovníku je, načte se příslušná fráze. Tato fráze se zapíše do souboru. Do slovníku přidej předchozímu použitému uzlu potomka s~hodnotou prvního znaku fráze. 

\subsection{Spustitelný klient}

Ladislav Zemek's avatar
Ladislav Zemek committed
Klient metody LZW může být spuštěn s~těmito přepínači:

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s~tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-nc <$e$>} -- tento přepínač říká, kolik maximálně frází muže být ve slovníku ($ 2^{e-1} $)
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item \textbf{-p <$n$>} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v~bytech)
Ladislav Zemek's avatar
Ladislav Zemek committed
Poznámka: Pokud  při kompresi některý z~posledních dvou přepínačů chybí nebo je v~některém chyba, metoda se zavolá s~výchozími parametry, které jsou: -nc  $17 $ -p $ 1 000 000 $.\newpage
Ladislav Zemek's avatar
Ladislav Zemek committed
\chapter{Testovaní}

Ladislav Zemek's avatar
Ladislav Zemek committed
Při určování kvality algoritmu se obvykle zjišťuje efektivita a časová náročnost. Efektivitu jsem se rozhodl vyjádřit pomocí kompresního poměru. Ten se vypočítá jako podíl velikostí zkomprimovaného a původního souboru. Časová naročnost je udaj, za kolik sekund program dosáhne správného výstupu.

Všechny tři metody jsou efektivní na datech, kde se opakují podřetězce, tedy například na běžném anglickém textu. Proto jsem se rozhodl k~testování použít korpus Calgary. Ten se skládá z~18 souborů s~anglickým textem o~celkové velikosti 3 190 KB. Abych demonstroval i nevýhody těchto algoritmů, jako druhý korpus použiji ten s~názvem Prague. Tak zvaný pražský korpus je specifický svou různorodostí. Obsahuje totiž soubory s~textem i s~náhodnými daty o~velikosti 56 900 KB. Dosáhnout dobrého kompresního poměru na pražském korpusu je obecně velmi těžké.

Všechna měření uvedená v~této kapitole byla pořízena na počítači s~těmito parametry:
 
Procesor -- Intel Core i7 (4 jádra s~frekvencí - 2,5 GHz)

RAM -- 8 GB

Operační systém -- Windows 10 (testování -- příkazová řádka Git Bash)


\section{Měření LZ77}

Jak je vidět v~tabulce měření, hlavní problém metody LZ77 je rostoucí časová složitost s~velikostí prohlížecího okna. Zjednodušeně by se dalo říct, že s~velikostí parametru roste složitost komprese exponencialně. Tomu odpovídají i naměřené časy. Komprese s~parametrem $ e $ trvá téměř dvojnásobný čas než s~$ e-1 $ a čtyřnásobný než s~$ e-2 $. Nicméně kompresní poměr je poměrně dobrý. Především u~korpusu Calgary. 

Zajímavým poznatkem měření je mírně klesající doba dekomprese. S~větším parametrem klesá kompresní poměr, což znamená, že se zmenšuje i počet tripletů v~souboru. Klesající tendence doby dekomprese je tedy způsobená menší režijí na načítání a zpracování tripletů. Z~dat v~tabulkách je také vidět, že složitost dekomprese nezávisí na $ e $.

V~tabulkách \ref{table:lz77Cal} a \ref{table:lz77Prag} můžete vidět statistiku běhu algoritmu LZ77 na korpusech Calgary a Prague v~závislosti na vstupním parametru $ e $. Ostatní parametry se při testování neměnily. Velikost aktualního okénka je 8 tedy 128 znaků a velikost částí je 2 miliony bytů (2 mb).

Z~obou tabulek je patrné, že s~rostoucím parametrem $ e $, se zlepšuje kompresní poměr ovšem doba běhu komprese exponencialně roste. Nicméně na korpusu Calgary došlo k~poměrně dobré kompresi již při parametru 12, což odpovídá prohlížecímu oknu velikosti 2048 znaků. Oproti tomu na Prague není komprese dobrá ani při parametru 15, což odpovídá 16384 znakům v~prohlížecím okně. Je také patrné, že při testování na Calgary se kompresní poměr zlepšuje rychleji než na Prague. Jinými slovy pomyslná křivka kompresního poměru klesá rychleji u~Calgary. 

Neefektivní kompresi na korpusu Prague způsobuje především velká náhodnost dat. Algoritmus nenachází delší prefixy a ve spoustě případů zapisuje triplet, který zabírá více místa než řetězec, který kóduje.

V~tabulce \ref{table:lz77Cal} uvádím pouze hodnoty parametru $ e $ od 10 do 17. Je tomu tak proto, že $ e < 10 $ se témeř nepoužívá, kvůli špatné efektivitě algoritmu a $ e > 17 $ už kompresi v~tomto případě nezlepšilo.V tabulce \ref{table:lz77Prag} je tomu pro $ e < 10 $. Pro $ e > 15 $ běží již algoritmus přiliž dlouho a je tedy nepoužitelný. Pro představu pokud $ e = 16 $ je to zhruba 10 minut a pro $ e = 17 $ zhruba 20 minut.

Ačkoliv se může z~tabulek zdát, že čím větší $ e $, tím lepší kompresní poměrn není tomu tak. Pro každý soubor existuje hranice, kdy už se komprese přestane zlepšovat. Způsobuje to zapisování čísel do tripletů. Parametrem $ e $ se nespecifikuje pouze velikost oken, ale i počet bitů potřebný pro zápis čísel do tripletu. Při příliš velkém $ e $ se stane, že pro zapisování tripletů bude používáno více bitů, ale jejich počet zůstane stejný jako u~menších $ e $. 

	\begin{table}[H]
		\centering
		\begin{tabular}{| l | l | l | l | l | l | l | l | l |}
			\hline
			Parametr $ e $ & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 \\ \hline
			Kompresní poměr & 0,75 & 0,65 & 0,60 & 0,57 & 0,53 & 0,51 & 0,49 & 0,47 \\ \hline
			Čas komprese (s) & 3,9 & 4,4 & 5,5 & 7,5 & 12,0 & 20,5 & 32,6 & 57,0 \\ \hline
			Čas dekomprese (s) & 3,5 & 3,1 & 3,0 & 2,9 & 3,1 & 3,1 & 3,0 & 2,8 \\
			\hline
		\end{tabular}
	\caption{testování LZ77 na Calgary}
	\label{table:lz77Cal}
	\end{table}
	\begin{table}[H]
		\centering
		\begin{tabular}{| l | l | l | l | l | l | l |}
			\hline
			Parametr $ e $ & 10 & 11 & 12 & 13 & 14 & 15 \\ \hline
			Kompresní poměr & 0,85 & 0,85 & 0,84 & 0,83 & 0,80 & 0,78 \\ \hline
			Čas komprese (s) & 30,9 & 42,6 & 62,5 & 103,0 & 180,0 & 330,5 \\ \hline
			Čas dekomprese (s) & 34,6 & 32,8 & 31,5 & 30,5 & 27,5 & 26,9 \\
			\hline
		\end{tabular}
	\caption{testování LZ77 na Prague}
	\label{table:lz77Prag}
	\end{table}

\section{Měření LZ78}

Podobně jako u~měření LZ77, i zde dopadlo lépe testování na Calgary. Korpus Prague má příliš náhodná data a slovník vytváří pouze krátké fráze.

U~této a LZW metody jsem měřil i hloubku stromů, které reprezentují slovník. Zatím co u~Calgary meli slovníky jednotlivých částí souboru obvykle hloubku kolem sta, u~Prague ve většině případů nepřesáhla deset. V~některých případech byla hloubka pouze tři. Což znamená, že nejdelší zakódovaný řetězec mel délku tři. To se muže zdát jako nesmysl, ale do prvních třech pater stromu se vejde ($ 256^{2} + 256^{3} $) podřetězců delky dva a tři. To je 16 842 752 uzlů, což více než osmkrát přesahuje maximální počet uzlů ve slovníku pro parametr 22. Na datech s~vysokou entropií může docházet k~těmto případům. Proto se tyto metody nepoužívají například pro komprimaci obrázků. Ve vetšině případů je totiž zvětšují.

Z~naměřených dat v~tabulkách \ref{table:lz78Cal} a \ref{table:lz78Prag} je vidět, že mé odhady složitosti jsou správné. Rychlost komprese i dekomprese je přibližně stejná a nezávisí na velikosti parametru $ e $. Nicméně čas komprese mírně roste s~vyšším parametrem $ e $, kdežto dekomprese mírně klesá. Zatímco u~komprese je to způsobeno větší režijí při přidávání frází do slovníku, u~dekomprese se jedná o~stejný důvod jako u~LZ77. S~vyšším parametrem se snižuje počet tripletů a tím i režije s~jejich načítáním a zpracováním.

Korpus Calgary dokázala metoda LZ77 zmenšit na 47~\% původní velikosti, což je nejlepší výsledek z~měření, nicméně metoda LZ78 zmenší Calgary na 51~\% za 35 krát kratší dobu než LZ77. Dá se tedy říci, že metoda LZ78 provádí podobně efektivní kompresi v~mnohem lepším čase.  

\begin{table}[H]
	\centering
	\begin{tabular}{| l | l | l | l | l | l | l |}
		\hline
		Parametr $ e $ & 16 & 17 & 18 & 19 & 20 & 21 \\ \hline
		Kompresní poměr & 0,67 & 0,62 & 0,60 & 0,54 & 0,53 & 0,51 \\ \hline
		Čas komprese (s) & 1,4 & 1,5 & 1,5 & 1,5 & 1,6 & 1,6 \\ \hline
		Čas dekomprese (s) & 1,9 & 1,8 & 1,8 & 1,7 & 1,7 & 1,7 \\
		\hline
	\end{tabular}
\caption{testování LZ78 na Calgary}
\label{table:lz78Cal}
\end{table}

\begin{table}[H]
	\centering
	\begin{tabular}{| l | l | l | l | l | l | l |}
		\hline
		Parametr $ e $ & 17 & 18 & 19 & 20 & 21 & 22 \\ \hline
		Kompresní poměr & 0,81 & 0,76 & 0,74 & 0,75 & 0,75 & 0,75 \\ \hline
		Čas komprese (s) & 13,4 & 13,9 & 13,9 & 14,5 & 14,6 & 15,0 \\ \hline
		Čas dekomprese (s) & 24,5 & 23,8 & 21,8 & 21,0 & 22,7 & 22,1 \\
		\hline
	\end{tabular}
\caption{testování LZ78 na Prague}
\label{table:lz78Prag}
\end{table}

\section{Měření LZW}

Metoda LZW je v mnoha ohledech podobná jako LZ78.

\begin{table}[H]
	\centering
	\begin{tabular}{| l | l | l | l | l | l | l | l |}
		\hline
		Parametr $ e $ & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\ \hline
		Kompresní poměr & 0,76 & 0,73 & 0,68 & 0,64 & 0,59 & 0,54 & 0,50 \\ \hline
		Čas komprese (s) & 1,4 & 1,4 & 1,4 & 1,5 & 1,5 & 1,6 & 1,6 \\ \hline
		Čas dekomprese (s) & 2,1 & 2,0 & 1,9 & 1,9 & 1,8 & 1,8 & 1,7 \\
		\hline
	\end{tabular}
\caption{testování LZW na Calgary}
\label{table:lzwCal}
\end{table}

\begin{table}[H]
	\centering
	\begin{tabular}{| l | l | l | l | l | l | l |}
		\hline
		Parametr $ e $ & 17 & 18 & 19 & 20 & 21 & 22 \\ \hline
		Kompresní poměr & 0,89 & 0,86 & 0,80 & 0,75 & 0,75 & 0,75 \\ \hline
		Čas komprese (s) & 12,3 & 12,8 & 14,2 & 14,7 & 15,3 & 15,5 \\ \hline
		Čas dekomprese (s) & 29,5 & 28,3 & 25,3 & 24,2 & 24,1 & 24,1 \\
		\hline
		
	\end{tabular}
\caption{testování LZW na Prague}
\label{table:lzwPrag}
\end{table}

Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{conclusion}
	%sem napište závěr Vaší práce
\end{conclusion}

\bibliographystyle{csn690}
Ladislav Zemek's avatar
Ladislav Zemek committed
\begin{thebibliography}{1}

\bibitem{komprese}
\textit{Komprese dat} [online]. [cit. 2018-04-22]. Wikipedie. Dostupné z:  \url{https://cs.wikipedia.org/wiki/Komprese_dat}

\bibitem{crush}
\textit{Crush} [online]. [cit. 2018-04-23]. Dostupné z: \url{https://sourceforge.net/projects/crush}

\bibitem{inspire}
\textit{LZ77 and LZ78} [online]. [cit. 2018-04-23]. Wikipedie. Dostupné z: \url{https://en.wikipedia.org/wiki/LZ77_and_LZ78}

\bibitem{lzwEN}
\textit{LZW} [online]. [cit. 2018-04-23]. Dostupné z: \url{https://www.prepressure.com/library/compression-algorithm/lzw}

\bibitem{req}
\textit{Analýza požadavků} [online]. [cit. 2018-04-23]. Wikipedie. Dostupné z: \url{https://cs.wikipedia.org/wiki/Anal\%C3\%BDza_po\%C5\%BEadavk\%C5\%AF}

Ladislav Zemek's avatar
Ladislav Zemek committed
\bibitem{vohoLZ77}
\textit{Algoritmus LZ77} [online]. [cit. 2018-04-25]. Dostupné z: \url{http://voho.eu/wiki/algoritmus-lz77/}

\bibitem{stringologyLZ78}
\textit{Algoritmus LZ78} [online]. [cit. 2018-04-28]. Dostupné z: \url{http://stringology.org/DataCompression/lz78/index_cs.html}

\bibitem{stringologyLZW}
\textit{Algoritmus LZW} [online]. [cit. 2018-04-28]. Dostupné z: \url{http://www.stringology.org/DataCompression/lzw-e/index_cs.html}

Ladislav Zemek's avatar
Ladislav Zemek committed
\end{thebibliography}
Ladislav Zemek's avatar
Ladislav Zemek committed

\appendix

\chapter{Seznam použitých zkratek}
% \printglossaries
\begin{description}
Ladislav Zemek's avatar
Ladislav Zemek committed
	\item[SCT] Small compression toolkit
	\item[LZ] Lempel-ziv
Ladislav Zemek's avatar
Ladislav Zemek committed
\end{description}


% % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% % Tuto kapitolu z výsledné práce ODSTRAŇTE.
% % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% 
% \chapter{Návod k~použití této šablony}
% 
% Tento dokument slouží jako základ pro napsání závěrečné práce na Fakultě informačních technologií ČVUT v~Praze.
% 
% \section{Výběr základu}
% 
% Vyberte si šablonu podle druhu práce (bakalářská, diplomová), jazyka (čeština, angličtina) a kódování (ASCII, \mbox{UTF-8}, \mbox{ISO-8859-2} neboli latin2 a nebo \mbox{Windows-1250}). 
% 
% V~české variantě naleznete šablony v~souborech pojmenovaných ve formátu práce\_kódování.tex. Typ může být:
% \begin{description}
% 	\item[BP] bakalářská práce,
% 	\item[DP] diplomová (magisterská) práce.
% \end{description}
% Kódování, ve kterém chcete psát, může být:
% \begin{description}
% 	\item[UTF-8] kódování Unicode,
% 	\item[ISO-8859-2] latin2,
% 	\item[Windows-1250] znaková sada 1250 Windows.
% \end{description}
% V~případě nejistoty ohledně kódování doporučujeme následující postup:
% \begin{enumerate}
% 	\item Otevřete šablony pro kódování UTF-8 v~editoru prostého textu, který chcete pro psaní práce použít -- pokud můžete texty s~diakritikou normálně přečíst, použijte tuto šablonu.
% 	\item V~opačném případě postupujte dále podle toho, jaký operační systém používáte:
% 	\begin{itemize}
% 		\item v~případě Windows použijte šablonu pro kódování \mbox{Windows-1250},
% 		\item jinak zkuste použít šablonu pro kódování \mbox{ISO-8859-2}.
% 	\end{itemize}
% \end{enumerate}
% 
% 
% V~anglické variantě jsou šablony pojmenované podle typu práce, možnosti jsou:
% \begin{description}
% 	\item[bachelors] bakalářská práce,
% 	\item[masters] diplomová (magisterská) práce.
% \end{description}
% 
% \section{Použití šablony}
% 
% Šablona je určena pro zpracování systémem \LaTeXe{}. Text je možné psát v~textovém editoru jako prostý text, lze však také využít specializovaný editor pro \LaTeX{}, např. Kile.
% 
% Pro získání tisknutelného výstupu z~takto vytvořeného souboru použijte příkaz \verb|pdflatex|, kterému předáte cestu k~souboru jako parametr. Vhodný editor pro \LaTeX{} toto udělá za Vás. \verb|pdfcslatex| ani \verb|cslatex| \emph{nebudou} s~těmito šablonami fungovat.
% 
% Více informací o~použití systému \LaTeX{} najdete např. v~\cite{wikilatex}.
% 
% \subsection{Typografie}
% 
% Při psaní dodržujte typografické konvence zvoleného jazyka. České \uv{uvozovky} zapisujte použitím příkazu \verb|\uv|, kterému v~parametru předáte text, jenž má být v~uvozovkách. Anglické otevírací uvozovky se v~\LaTeX{}u zadávají jako dva zpětné apostrofy, uzavírací uvozovky jako dva apostrofy. Často chybně uváděný symbol "{} (palce) nemá s~uvozovkami nic společného.
% 
% Dále je třeba zabránit zalomení řádky mezi některými slovy, v~češtině např. za jednopísmennými předložkami a spojkami (vyjma \uv{a}). To docílíte vložením pružné nezalomitelné mezery -- znakem \texttt{\textasciitilde}. V~tomto případě to není třeba dělat ručně, lze použít program \verb|vlna|.
% 
% Více o~typografii viz \cite{kobltypo}.
% 
% \subsection{Obrázky}
% 
% Pro umožnění vkládání obrázků je vhodné použít balíček \verb|graphicx|, samotné vložení se provede příkazem \verb|\includegraphics|. Takto je možné vkládat obrázky ve formátu PDF, PNG a JPEG jestliže používáte pdf\LaTeX{} nebo ve formátu EPS jestliže používáte \LaTeX{}. Doporučujeme preferovat vektorové obrázky před rastrovými (vyjma fotografií).
% 
% \subsubsection{Získání vhodného formátu}
% 
% Pro získání vektorových formátů PDF nebo EPS z~jiných lze použít některý z~vektorových grafických editorů. Pro převod rastrového obrázku na vektorový lze použít rasterizaci, kterou mnohé editory zvládají (např. Inkscape). Pro konverze lze použít též nástroje pro dávkové zpracování běžně dodávané s~\LaTeX{}em, např. \verb|epstopdf|.
% 
% \subsubsection{Plovoucí prostředí}
% 
% Příkazem \verb|\includegraphics| lze obrázky vkládat přímo, doporučujeme však použít plovoucí prostředí, konkrétně \verb|figure|. Například obrázek \ref{fig:float} byl vložen tímto způsobem. Vůbec přitom nevadí, když je obrázek umístěn jinde, než bylo původně zamýšleno -- je tomu tak hlavně kvůli dodržení typografických konvencí. Namísto vynucování konkrétní pozice obrázku doporučujeme používat odkazování z~textu (dvojice příkazů \verb|\label| a \verb|\ref|).
% 
% \begin{figure}\centering
% 	\includegraphics[width=0.5\textwidth, angle=30]{cvut-logo-bw}
% 	\caption[Příklad obrázku]{Ukázkový obrázek v~plovoucím prostředí}\label{fig:float}
% \end{figure}
% 
% \subsubsection{Verze obrázků}
% 
% % Gnuplot BW i barevně
% Může se hodit mít více verzí stejného obrázku, např. pro barevný či černobílý tisk a nebo pro prezentaci. S~pomocí některých nástrojů na generování grafiky je to snadné.
% 
% Máte-li například graf vytvořený v programu Gnuplot, můžete jeho černobílou variantu (viz obr. \ref{fig:gnuplot-bw}) vytvořit parametrem \verb|monochrome dashed| příkazu \verb|set term|. Barevnou variantu (viz obr. \ref{fig:gnuplot-col}) vhodnou na prezentace lze vytvořit parametrem \verb|colour solid|.
% 
% \begin{figure}\centering
% 	\includegraphics{gnuplot-bw}
% 	\caption{Černobílá varianta obrázku generovaného programem Gnuplot}\label{fig:gnuplot-bw}
% \end{figure}
% 
% \begin{figure}\centering
% 	\includegraphics{gnuplot-col}
% 	\caption{Barevná varianta obrázku generovaného programem Gnuplot}\label{fig:gnuplot-col}
% \end{figure}
% 
% 
% \subsection{Tabulky}
% 
% Tabulky lze zadávat různě, např. v~prostředí \verb|tabular|, avšak pro jejich vkládání platí to samé, co pro obrázky -- použijte plovoucí prostředí, v~tomto případě \verb|table|. Například tabulka \ref{tab:matematika} byla vložena tímto způsobem.
% 
% \begin{table}\centering
% 	\caption[Příklad tabulky]{Zadávání matematiky}\label{tab:matematika}
% 	\begin{tabular}{|l|l|c|c|}\hline
% 		Typ		& Prostředí		& \LaTeX{}ovská zkratka	& \TeX{}ovská zkratka	\tabularnewline \hline \hline
% 		Text		& \verb|math|		& \verb|\(...\)|	& \verb|$...$|		\tabularnewline \hline
% 		Displayed	& \verb|displaymath|	& \verb|\[...\]|	& \verb|$$...$$|	\tabularnewline \hline
% 	\end{tabular}
% \end{table}
% 
% % % % % % % % % % % % % % % % % % % % % % % % % % % % 

\chapter{Obsah přiloženého CD}

%upravte podle skutecnosti

\begin{figure}
	\dirtree{%
		.1 readme.txt\DTcomment{stručný popis obsahu CD}.
		.1 exe\DTcomment{adresář se spustitelnou formou implementace}.
		.1 src.
		.2 impl\DTcomment{zdrojové kódy implementace}.
		.2 thesis\DTcomment{zdrojová forma práce ve formátu \LaTeX{}}.
		.1 text\DTcomment{text práce}.
		.2 thesis.pdf\DTcomment{text práce ve formátu PDF}.
		.2 thesis.ps\DTcomment{text práce ve formátu PS}.
	}
\end{figure}

\end{document}