Texty, které obsahují mnoho po sobě se opakujících identických znaků, se dobře komprimují pomocí metody Run-Length Encoding (RLE), ve které se každé takové opakování nahradí uvedením počtu opakování daného znaku, například takto:
Takové texty ale nejsou moc časté – výrazně více se vyskytují texty, které obsahují mnoho opakování stejných podřetězců (a ne tedy pouze znaků). Burrowsova-Wheelerova transformace (BWT) je způsob, jak takové obecné texty připravit do podoby vhodné pro zkomprimování pomocí RLE, a přitom pochopitelně zachovat možnost rekonstrukce původního řetězce.
BWT vezme vstupní řetězec, který doplní o jednoznačné označení jeho konce (znakem nevyskytujícím se v samotném řetězci) a následně ho ztransformuje do jiného řetězce, který je vhodnější pro RLE, přičemž se z něj původní řetězec dá inverzní BWT zase zrekonstruovat.
Při komprimaci vstupního řetězce text je tedy postup..
text → BWT(text) → RLE(BWT(text))
..zatímco při dekompresi zakódovaných dat data zase:
iRLE(data) → iBWT(iRLE(data)) → text
i
značí inverzní verze příslušných transformací.
Začněmež převedením vstupního textu do jeho BWT-podoby, tedy textu stejně dlouhého, jen jistým způsobem uspořádaného. Pro příklad použijeme řetězec ABRAKADABRA
, který doplníme na konci znakem $
.
Z důvodů, které se osvětlí později, s každým písmenem zaznamenáme zároveň i jeho pořadí v rámci stejných znaků.
A
bude mít indexy 1 až 5, protože se ve vstupním textu vyskytuje právě pětkrát.
Prvním krokem BWT je vytvoření všech cyklických záměn vstupního textu (kterých je zjevně stejně jako činí jeho délka; včetně prodloužení o znak ukazatele konce). Zajímat nás bude pouze první a poslední sloupeček, proto si je zvýrazněmež:
A1B1R1A2K1A3D1A4B2R2A5$1 B1R1A2K1A3D1A4B2R2A5$1A1 R1A2K1A3D1A4B2R2A5$1A1B1 A2K1A3D1A4B2R2A5$1A1B1R1 K1A3D1A4B2R2A5$1A1B1R1A2 A3D1A4B2R2A5$1A1B1R1A2K1 D1A4B2R2A5$1A1B1R1A2K1A3 A4B2R2A5$1A1B1R1A2K1A3D1 B2R2A5$1A1B1R1A2K1A3D1A4 R2A5$1A1B1R1A2K1A3D1A4B2 A5$1A1B1R1A2K1A3D1A4B2R2 $1A1B1R1A2K1A3D1A4B2R2A5
Druhým krokem je seřazení těchto řetězců podle abecedy (lexikograficky):
$1A1B1R1A2K1A3D1A4B2R2A5 A5$1A1B1R1A2K1A3D1A4B2R2 A4B2R2A5$1A1B1R1A2K1A3D1 A1B1R1A2K1A3D1A4B2R2A5$1 A3D1A4B2R2A5$1A1B1R1A2K1 A2K1A3D1A4B2R2A5$1A1B1R1 B2R2A5$1A1B1R1A2K1A3D1A4 B1R1A2K1A3D1A4B2R2A5$1A1 D1A4B2R2A5$1A1B1R1A2K1A3 K1A3D1A4B2R2A5$1A1B1R1A2 R2A5$1A1B1R1A2K1A3D1A4B2 R1A2K1A3D1A4B2R2A5$1A1B1
Takto vzniklý poslední (červený) sloupeček je právě BWT-obrazem vstupního řetězce.
Pro vstup ABRAKADABRA$
jsme tedy po provedení Burrows-Wheelerovy transformace obdrželi řetězec ARD$KRAAAABB
.
Pro takto krátký řetězec nemá samozřejmě provedení RLE příliš smysl (dostali bychom A1R1D1$1K1R1A4B2
, což je dokonce delší řetězec), ale pro (výrazně) delší už by úspora mohla být významná.
Na první pohled vypadá výstup BWT ARD$KRAAAABB
zcela netransformovatelně do původního stavu. Nicméně BWT má jednu velmi zajímavou vlastnost (kvůli které jsme si zapisovali ty indexy) – tzv. First-Last Property, která nám pomůže s rekonstrukcí:
K-tý výskyt jistého symbolu v BWT-řetězci a k-tý výskyt téhož symbolu v prvním sloupci vytvořující matice odpovídají oba stejnému symbolu ve vstupním textu.
Což mimo jiné znamená, že stejné znaky se v BWT-řetězci vyskytují ve stejném pořadí jako v prvním sloupečku vytvořující matice transformace.
A
v řetězci BWT vyskytuje s indexy v pořadí 5-4-1-3-2, přičemž v odpovídajícím prvním sloupečku je jejich pořadí úplně stejné (a ještě navíc jsou díky abecednímu uspořádání přesně za sebou).
$1..........A5 A5..........R2 A4..........D1 A1..........$1 A3..........K1 A2..........R1 B2..........A4 B1..........A1 D1..........A3 K1..........A2 R2..........B2 R1..........B1
Vypůjčme si z naší vytvořující matice Burrows-Wheelerovy transformace..
$1A1B1R1A2K1A3D1A4B2R2A5 A5$1A1B1R1A2K1A3D1A4B2R2 A4B2R2A5$1A1B1R1A2K1A3D1 A1B1R1A2K1A3D1A4B2R2A5$1 A3D1A4B2R2A5$1A1B1R1A2K1 A2K1A3D1A4B2R2A5$1A1B1R1 B2R2A5$1A1B1R1A2K1A3D1A4 B1R1A2K1A3D1A4B2R2A5$1A1 D1A4B2R2A5$1A1B1R1A2K1A3 K1A3D1A4B2R2A5$1A1B1R1A2 R2A5$1A1B1R1A2K1A3D1A4B2 R1A2K1A3D1A4B2R2A5$1A1B1
..pouze řádky začínající na písmeno A
:
A5$1A1B1R1A2K1A3D1A4B2R2 A4B2R2A5$1A1B1R1A2K1A3D1 A1B1R1A2K1A3D1A4B2R2A5$1 A3D1A4B2R2A5$1A1B1R1A2K1 A2K1A3D1A4B2R2A5$1A1B1R1
Jelikož řádky jsou již lexikograficky uspořádané, provedeme-li jejich levou rotaci a přesuneme tak písmena A
ze začátku na konec, na uspořádání řádek se nic nezmění:
$1A1B1R1A2K1A3D1A4B2R2A5 B2R2A5$1A1B1R1A2K1A3D1A4 B1R1A2K1A3D1A4B2R2A5$1A1 D1A4B2R2A5$1A1B1R1A2K1A3 K1A3D1A4B2R2A5$1A1B1R1A2
Tímto postupem jsme získali přesně ty řádky vytvořující matice, které na písmeno A
končí a to nepřekvapivě přesně ve správném pořadí! K-tý výskyt jistého znaku v prvním sloupci tedy odpovídá jeho k-tému výskytu ve sloupci posledním (tedy v jeho BWT-podobě), což bylo dokázati.
Trik je nyní v tom, že z BWT-řetězce snadno vytvoříme odpovídající první sloupeček vytvořující matice – prostě ho seřadíme podle abecedy:
1 $1..........A5 2 A5..........R2 3 A4..........D1 4 A1..........$1 5 A3..........K1 6 A2..........R1 7 B2..........A4 8 B1..........A1 9 D1..........A3 10 K1..........A2 11 R2..........B2 12 R1..........B1
A nyní si už stačí jenom všimnout, že:
Znak z prvního sloupečku je znakem, který při cyklické záměně bude následovat po znaku z posledního sloupečku (tedy z BWT-řetězce).
No a indexy nám umožňují dohledat, kterému znaku v posledním sloupečku (tedy v BWT-řetězci) tento znak z prvního sloupečku odpovídá! Čili stačí začít od ukončovacího znaku v posledním sloupečku (zde $
) a postupně pokračovat v rekonstrukci po správných písmenech.
A
v BWT-řetězci odpovídá právě druhé A
v prvním sloupci.
Postup rekonstrukce je tedy následující:
$1
tedy následuje A1
(čtvrtý řádek) ⇒ aktuálně zrekonstruovná část vstupního řetězce jest $1A1
.
A1
následuje B1
(osmý řádek) ⇒ $1A1B1
.
B1
následuje R1
(dvanáctý řádek) ⇒ $1A1B1R1
.
R1
následuje A2
(šestý řádek) ⇒ $1A1B1R1A2
.
Až je výsledkem nakonec první řádek vytvořující matice, tedy řetězec $1A1B1R1A2K1A3D1A4B2R2A5
, což je – až na umístění znaku $
– přesně náš původní řetězec ^_^