Beruflich Dokumente
Kultur Dokumente
Por DARK-N
naldo_go@hotmail.com
Julio del 2004/Revisado: Dic/2006
V1.1: Algoritmo LZ77 explicado
http://bicharraco.dyndns.org/darkn
NOTA 1: Todo lo que hay aquí es TEORIA, nada de cómo implementarlo en ASM.
NOTA 2: Lo que investigue estaba todo en ingles y portugués por lo que pueda que tenga errores en
algunas cosas que yo las entendí de cierta forma y quizás no es así, por lo que sugiero que si alguien
sabe mi error (si es que lo hay) que me mande un mail.
CONTENIDO
Introducción................................................................................................................................2
Algoritmo de Huffman................................................................................................................3
Como Funciona Teóricamente................................................................................................3
Como funciona en la práctica con un Ejemplo.......................................................................3
LZ (Lempel-Ziv).........................................................................................................................6
Como Funciona Teóricamente................................................................................................6
Como funciona en la práctica con un Ejemplo.......................................................................7
La Descompresión..................................................................................................................9
LZ77..........................................................................................................................................10
Conceptos previos.................................................................................................................10
DECODIFICACIÓN.............................................................................................................12
MEJORAS DE LZ77:...........................................................................................................13
2
Introducción
La compresión busca reducir el número de bits para almacenar o transmitir información. Los
compresores de datos se dividen en 2 grandes familias: Los compresores SIN pérdida de la
información (Losless) y los compresores CON pérdida de información (Lossy).
Los compresores con pérdida son utilizados para comprimir representaciones digitales con señales
análogas como por ejemplo sonidos o imágenes (les suena mp3 o jpeg). Esto es ya que los datos si
los descomprimimos no necesariamente deben ser una copia exacta de los originales debido a que
esta perdida de información no es importante para el sistema audio/visual humano.
En cambio los compresores sin pérdida de información garantizan la regeneración exacta de los datos
comprimidos, al momento de realizar la descompresión ya que no se ha perdido ningún tipo de
información. Esta es la compresión que se usa cuando se almacenan archivos con caracteres ASCII,
registros de una base de datos, información de una hoja de cálculo, etc. Estos compresores son los
llamados “compresores de Texto”.
La compresión sin pérdida de información se ocupa para comprimir texto, y existen varios algoritmos
para estos casos, los cuales los mas importante son LZ77 y LZ88 (con sus variantes LZW y LZH) y el
famoso Huffman muy conocido solo “de nombre” por nosotros, los traductores de juegos.
Run-length encoding
- PackBits
- PCX
Dictionary coders
- DEFLATE
- LZ77 & LZ78
- LZW
- Other LZ compression methods
Compresión Fractal
- Fractal transform
Compresión Wavelet
3
Algoritmo de Huffman
(Para este documento me base en uno hecho por Salvador Pozo, pero jamás hice un Copy-Paste)
Un clásico algoritmo usado en muchos juegos de SNES para comprimir texto. Fue creado por David
A. Huffman en 1952 y es usado ampliamente en programas de compresión como GZIP, máquinas de
FAX, esquemas de compresión de imágenes como el conocido JPEG, etc.
Este algoritmo utiliza el método llamado la propiedad de estadística de alfabetos en fuentes de
corriente (the statistical property of alphabets in source stream) [Walley, 1997]
Este algoritmo se basa en asignar códigos de distinta longitud de bits a cada uno de los caracteres de
un fichero. Si se asignan códigos más cortos a los caracteres que aparecen más a menudo se
consigue una compresión del archivo.
Esta compresión es mayor cuando la variedad de caracteres diferentes que aparecen es menor. Por
ejemplo: si el texto se compone únicamente de números o mayúsculas, se conseguirá una
compresión mayor.
Contar cuantas veces aparece cada carácter en el fichero a comprimir. Y crear una lista
enlazada con la información de caracteres y frecuencias.
Ordenar la lista de menor a mayor en función de la frecuencia.
Convertir cada elemento de la lista en un árbol.
Fusionar todos estos árboles en uno único, para hacerlo se sigue el siguiente proceso,
mientras la lista de árboles contenga más de un elemento:
o Con los dos primeros árboles formar un nuevo árbol, cada uno de los árboles
originales en una rama.
o Sumar las frecuencias de cada rama en el nuevo elemento árbol.
o Insertar el nuevo árbol en el lugar adecuado de la lista según la suma de frecuencias
obtenida.
Para asignar el nuevo código binario de cada carácter sólo hay que seguir el camino
adecuado a través del árbol. Si se toma una rama cero, se añade un cero al código, si se
toma una rama uno, se añade un uno.
Se recodifica el fichero según los nuevos códigos.
1) Contamos las veces que aparece cada carácter y hacemos una lista enlazada:
4
4) Fundimos los dos primeros nodos (árboles) en un nuevo árbol, sumamos sus frecuencias y lo
colocamos en el lugar correspondiente, a este árbol se le llama Árbol Huffman:
Y sucesivamente:
5) Asignamos los códigos, las ramas a la izquierda son ceros, y a la derecha unos (por ejemplo), es
una regla arbitraria. Recuerda que el carácter con mayor probabilidad tiene menor número de bits
asignados, para que la cantidad de bits finales sea menor.
a '' c l t s e j
0 10 1100 1101 1110 11110 111110 111111
6) Y traducimos el texto:
Pero no nos engañemos, también hay que almacenar la información relativa a la codificación, por lo
que se puede ver que para textos cortos no obtendremos mucha reducción de tamaño.
6
LZ (Lempel-Ziv)
Este algoritmo (es en verdad un estilo de algoritmo) fue descubierto por Jakob Ziv y Abraham Lempel
en 1977 y 1978. Existen 3 tipos de algoritmos LZ, estos son LZ77, LZ78 y LZSS.
LZW (Lempel-Ziv Welch) es una variante de LZ78 y que es usado en programas de compresión como
el compress de UNIX.
LZH es una modificación de LZ77 que incluye un segundo nivel de compresión usando codificación
de Huffman.
En esta guía se explicará LZ77 ya que es usado en compresión de gráficos y textos de SNES. La
particularidad de LZ es que además de tomar en cuenta los caracteres uno a uno de una frase o
palabra, sino que además se consideraran aquellas secuencias de alta probabilidad en el texto. Por
ejemplo, en el texto:
aaabbaabaa
Los "_" son Espacios. Este es el mensaje original son 43 bytes, o 43 x 8 = 344 bits de largo.
Lo primero que hace LZ77 es buscar una combinación (o match) repetidos y que estan JUNTAS así
en el ejemplo avanzamos letra a letra (de 1 avanzando) y nos encontramos con rain_in_ es decir:
the_rain_
y seguido: in_
Paso 1: El buffer delantero leyó 9 bytes (the_rain_) y luego se encuentra con una combinación
entonces the_rain_pasa a ser parte de la ventana sliding y el buffer delantero ahora es “in_”.
Paso 2: Entonces tomando el contenido del buffer delantero se busca la mejor combinación dentro del
la ventana sliding y se encuentra (in_).
Paso 4: Como el largo del match es 3 (in_) y es mayor al mínimo largo del match, 2 (ya que 2
caracteres deben comprimirse como mínimo), entonces se representa con un puntero que nos indica
el largo y la distancia hacia atrás donde esta la combinación repetida, es un puntero <largo,
distancia>. En tutoriales en ingles le llaman pointer <length, offset>. De esta forma tenemos:
the_rain_<3,3>
Un puntero de 8-bit + 4-bit largo, el cual asume un máximo distancia (offset) de 255 y un largo
(length) de 15.
Un puntero de 12-bit + 6-bit largo, que asume un máximo distancia de 4096, lo que implica un
buffer de 4 KB, y un largo máximo de 63.
También hay un bit FLAG con un valor de 0 que indica un puntero. Este es seguido por un segundo bit
que indica el formato del puntero que se usara, con un 0 indica el un puntero de 8-bit y un 1 indica a
uno de 12-bit. De esta forma, bináriamente el puntero <3,3> es así:
00 00000011 0011
Los primeras 2 bits son las Flag, indican un puntero de 8 bit. Los siguientes 8 bits son la distancia o
valor del puntero (3), y los ultimos 4 son el largo.
8
Sp
the_rain_<3,3>Sp
Ahora nos damos cuenta que los caracteres "ain_" de “Spain” en “rain” por lo que se comprimen con
un puntero:
the_rain_<3,3>Sp<9,4>
El <9,4> es que tensmos una distancia hacia atras de 9 y un largo (ain_) de 4. La distancia es 9 ya
que si nos situamos en la letra a de “Spain_” y vamos 9 espacios hacia atrás nos encontramos con la
letra a de “rain_”.
Seguimos leyendo y nos encontramos con “falls_mainly_” por lo que nos da:
the_rain_<3,3>Sp<9,4>falls_m<11,3>
Nota que hacemos referencia a “Spain” y NO a rain, es decir a la ocurrencia mas cercana por eso nos
da <11,3>. Esto es para que nos de un puntero mas pequeño.
Los caracteres "ly" son lacados sin compresión pero "in_" y "the_" si se comprimen cada uno con su
puntero, quedando:
the_rain_<3,3>Sp<9,4>falls_m<11,3>ly_<16,3><34,4>
Finalmente los caracteres "pl" se sacan sin comprimir, y "ain" nuevamente se comprime quedando:
the_rain_<3,3>Sp<9,4>falls_m<11,3>ly_<16,3><34,4>pl<15,3>
9
Esto nos da 23 caracteres sin comprimir con 9 bits cada uno, + 6 punteros de 14 bits, son un total de
291 bits.
Explicación:
Cada carácter sin comprimir es de 9 bit ya que al aplicarle este método se le agrega
información. Originalmente cada carácter es de 8 bytes y se le agrega un 1 bit por el FLAG
que indica 1 si NO hay puntero o 0 si hay puntero. Por esto hay raras veces que un archivo
comprimido tiene un tamaño mayor que el original.
Los punteros son de 12 Bits (primer formato) ya que ninguno supero una distancia de 255 y
se le agregan 2 bits más por los 2 Flag de Largo y Distancia. En total 12 + 2 = 14 bits.
Si los comparamos con los 344 bits que teníamos en un principio, ¡logramos una compresión! Esta no
es una mala compresión a pesar de ser tan corto el mensaje, pero en la práctica se aplica con textos
mucho más largos.
La Descompresión
El texto se descomprime como se ve arriba, entonces se sigue avanzando. Luego nos encontramos
con <9,4>, bajamos 9 espacios y copiamos 4, es decir “ain_”, ahora nos queda:
y así sucesivamente, hasta que tenemos el texto completo descomprimido con sus 43 caracteres (43
bytes):
10
LZ77
(Extraído y mejorado del original del PPT de ROQUE MARÍN roque@dif.um.es)
Conceptos previos
-Se mueve un puntero de búsqueda hacia atrás sobre el buffer de búsqueda hasta que encuentra un
carácter que concuerda con el primero del buffer de anticipación
-Comprueba si los caracteres que vienen a continuación del puntero concuerdan con los caracteres
consecutivos del buffer de anticipación
-El número de caracteres consecutivos en el buffer de búsqueda que concuerdan con caracteres
consecutivos del buffer de anticipación se llama longitud (l)
-Una vez que ha encontrado la coincidencia de mayor longitud, la codifica con una tripleta <o, l, c>,
donde c es la palabra código correspondiente al carácter del buffer de anticipación que viene a
continuación de la coincidencia
EJEMPLO:
El C(r) nos dice que se desplaza la ventana deslizante (el buffer de búsqueda) 5 caracteres y nos
encontramos en el Buffer de Anticipación con una r, entonces recorremos el buffer de búsqueda
buscando una r.
12
Como esta ultima es la coincidencia más larga., se transmite la tripleta <3, 5, C(d)>
DECODIFICACIÓN
La primera tripleta <0, 0, C(d)> indica que no hay coincidencias y que hay que generar el carácter d
La segunda tripleta <7, 4, C(r)> indica que hay que mover el puntero 7 posiciones hacia atrás. Y hay
que copiar 4 caracteres a partir del puntero.
La tercera tripleta <3, 5, C(d)> indica que hay que mover el puntero 3 posiciones hacia atrás
Y hay que copiar 5 caracteres a partir del puntero. Se comienza copiando 3 caracteres.
13
MEJORAS DE LZ77:
Hemos asumido que las tripletas se codifican con secuencias de longitud fija (por ejemplo, tres bytes)
La codificación de tripletas del tipo <0, 0, C(d)> es ineficiente, sobretodo si aparece un gran número
de caracteres infrecuentes.
Mejora: Usar un bit de flag que indica si lo que sigue es la palabra código de un solo carácter
Así se transmiten sólo palabras código C(d) o parejas <o, l>, y se intercala el bit de flag cada vez que
se cambia de unas a otras.
El algoritmo LZ77 parte de la suposición de que los patrones recurrentes aparecen próximos :
Sólo busca en el “pasado cercano” (dentro del buffer de búsqueda)
Desaprovecha las recurrencias “lejanas”
El peor caso: un archivo con un patrón periódico, pero con un periodo mayor que la longitud del buffer
Ejemplo: En este caso, cada carácter iría como una tripleta (expande en lugar de comprimir)
i es un índice que indica la posición dentro del diccionario de la secuencia previa más larga
coincidente con la entrada
C es el código del carácter que viene a continuación de la secuencia coincidente
Si no se encuentra coincidencia, i = 0
Cada nueva pareja generada <i, C> se añade al diccionario.
Por Dark-N
naldo_go@hotmail.com