Método de Burrows-Wheeler

del.icio.us del.icio.us
Digg Digg
Furl Furl
Reddit Reddit
Rojo Rojo
Add to OnlyWire

O Método de Burrows-Wheeler , também conhecido pela sigla em inglês BWT, é um processamento estatístico de um bloco de dados que aumenta a redundância espacial, facilitando a aplicação de técnicas de compressão de dados.

Diferentemente de outras técnicas usadas par a compressão de dados que trabalham com um fluxo contínuo de bytes tratados um a um, o método de Burrows-Wheeler trabalha com blocos geralmente grandes de dados que são processados em conjunto antes de serem comprimidos.

Índice

Funcionamento

A teoria por traz do método de Burrows-Wheeler é que dado um símbolo presente no bloco de dados, existe uma probabilidade grande deste símbolo ser sempre precedido do mesmo conjunto de símbolos. Por exemplo, na língua inglesa: a letra "H" tem maior probabilidade de ser precedida pela letra "T" do que por qualquer outra letra. Usando este princípio, o método tenta construir uma seqüência de caracteres L que satisfaça as seguintes condições[1]:

  1. Qualquer região de L tende a apresentar uma concentração de apenas poucos símbolos. Isso permite que L seja facilmente comprimido por técnicas de move-to-front e RLE.
  2. É possível reconstruir o bloco de dados original com pouca ou nenhuma informação adicional.

A forma como o método de Burrows-Wheeler atinge este objetivo é a seguinte: em primeiro lugar, constrói-se uma matriz n x n onde n é o tamanho do bloco a ser comprimido. Esta matriz é preenchida na primeira linha com o bloco original; na segunda linha com o bloco rotacionado a esquerda de uma posição; na terceira linha com o bloco rotacionado de 2 posições, e assim por diante, até termos na última linha o bloco rotacionado de n-1 posições. O exemplo abaixo demonstra o processo:

a_asa_da_casa
_asa_da_casaa
asa_da_casaa_
sa_da_casaa_a
a_da_casaa_as
_da_casaa_asa
da_casaa_asa_
a_casaa_asa_d
_casaa_asa_da
casaa_asa_da_
asaa_asa_da_c
saa_asa_da_ca
aa_asa_da_cas 

O segundo passo é agora reordenar esta matriz em ordem lexicográfica, obtendo então a seguinte matriz:

 0: _asa_da_casaa
 1: _casaa_asa_da
 2: _da_casaa_asa
 3: a_asa_da_casa <- linha original
 4: a_casaa_asa_d
 5: a_da_casaa_as
 6: aa_asa_da_cas
 7: asa_da_casaa_
 8: asaa_asa_da_c
 9: casaa_asa_da_
10: da_casaa_asa_
11: sa_da_casaa_a
12: saa_asa_da_ca

Desta nova matriz podemos preencher as condições dadas mais acima com apenas duas informações: a última coluna, que preenche a condição 1, e o número I da linha que corresponde ao bloco original (neste caso, a linha 3, pois iniciamos a contagem em 0). Podemos ver que na última coluna desta nova matriz os simbolos "a" e "_" se acumularam em uma parte específica da matriz. Em um bloco de tamanho maior, este efeito é ainda mais acentuado, melhorando ainda mais a eficiência dos métodos de compressão.

Os dados que precisamos comprimir então são a linha L = "aaaadss_c__aa" e o número I = 3

Operação reversa

Reconstruir a primeira coluna da matriz ordenada a partir dos dados comprimidos (a última coluna e o número da linha original) é um processo fácil: basta reordenar a linha L que acabamos de descomprimir, obtendo assim "___aaaaaacdss". Durante este processo de reordenação, construímos um novo vetor que mapeia as posições da letra na L antiga para a sua respectiva posição na cadeia ordenada. Por exemplo, a primeira posição da cadeia, letra "a" se encontra na posição 3 da cadeia ordenada, a segunda letra "a" se encontra na posição 4 da cadeia ordenada, e assim por diante, obtendo-se então o vetor T = (3,4,5,6,10,11,12,0,9,1,2,7,8).

Com este vetor T e o valor I armazenado junto com a cadeia comprimida podemos então reconstruir a cadeia original S pela seguinte fórmula: S[n - 1 - i] \gets L[T^i[I]], para i = 0,1,...,n − 1 onde T0[j] = j, e Ti + 1[j] = T[Ti[j]].

A reconstrução dos primeiros elementos da cadeia original fica então sendo:

 i = 0 \Rightarrow S[13-1-0] = L[T^0[I]]=L[T^0[3]] = L[3] = \mbox{a},
 i = 1 \Rightarrow S[13-1-1] = L[T^1[I]]=L[T[T^0[I]]] = L[T[3]] = L[6] = \mbox{s}...

Aplicações

O método de Burrows-Wheeler foi difundido principalmente pelo utilitário de compactação de dados bzip2.

Exemplo de implementação

#!/usr/bin/python
# coding: utf-8
 
import sys
 
class bwt_encoder:
    """
        Transforma um bloco de caracteres em uma seqüência 
        mais facilmente comprimível usando o método de
        Burrows-Wheeler, de acordo com o descrito em
        http://pt.wikipedia.org/wiki/Método_de_Burrows-Wheeler
    """
    def get_permutations(self, str):
        """
            Cria as permutações de um bloco que são
            necessárias ao BWT.            
        """
        ret = []
        for i in range(0, len(str)):
            ret = ret + [str[i:] + str[0:i]]
        return ret
    def encode(self, str):
        """
            A "codificação" coresponde simplesmente a se
            selecionar a última coluna da matriz de 
            permutações ordenadas lexicograficamente, 
            além de informa a posição da cadeia original
            nesta matriz de permutações.
        """
        perms = self.get_permutations(str)
        perms.sort()
        last_column = ''
        for line in perms:
            last_column = last_column + line[len(line)-1]
        index = 0
        for index in range(0, len(perms)):
            if perms[index] == str:
                break
        return (index, last_column)
 
class bwt_decoder:
    """
        Faz a transformação reversa da 
        descrita em bwt_encode.
    """
    def get_indexes(self, str, sorted):
        """
            Os índices mapeiam cada símbolo da cadeia
            "codificada" com os símbolos da cadeia 
            ordenada. Esta lista de índices é o
            elemento essencial na transformação reversa.
        """
        used_pos = dict()
        indexes = []
        for i in range(0, len(str)):
            for j in range(0, len(sorted)):
                if sorted[j] == str[i] and (not used_pos.has_key(j)):
                    used_pos[j] = True
                    indexes = indexes + [j]
                    break
        return indexes
    def decode(self, str, index):
        """
            Usando a lista de índices calculadas no método
            get_indexes e o índice correspondentes a linha
            original na matriz, reconstruímos a linha 
            original, que corresponde ao arquivo decodificado.
        """
        sorted = [str[i] for i in range(0, len(str))]
        sorted.sort()
        indexes = self.get_indexes(str, sorted)
        ret = ''
        T = index
        for i in range(0, len(str)):
            char = str[T]
            ret = char + ret
            T = indexes[T]
        return ret
 
if __name__ == "__main__":
    str = 'a_asa_da_casa'
 
    # encode
    encoder = bwt_encoder()
    (index, last_column) = encoder.encode(str)
    print ('encoded:', index, last_column)
 
 
    # decode
    decoder = bwt_decoder()
    decoded = decoder.decode(last_column, index)
    print ('decoded:', decoded)

Referências

  1. SALOMON, David. Data Compression: The Complete Reference. 2.ed. Nova Iorque: Springer, 2000. ISBN 0-387-95045-1 página 682.

This article is from Wikipedia. All text is available under the terms of the GNU Free Documentation License.


Giant Panda

Mercedes Car
James Bond Guide
This site monitored by SitePinger.net