Está en la página 1de 21

Capı́tulo 4

Técnicas de Diccionario

Cuando tratamos con textos de algún lenguaje de programación, entre


otros ejemplos, determinados grupos de letras son muy frecuentes y, por el
contrario, otras combinaciones no se dan o son muy raras. Esta caracterı́stica
puede explotarse para conseguir una mayor compresión. Lo usual es elaborar
una lista o diccionario con las combinaciones más frecuentes de letras del
alfabeto fuente y las palabras-código correspondientes. Por lo general, las
palabras-código serán de igual longitud.
El diccionario puede ser estático o dinámico. Cuando se tiene un buen
conocimiento a priori de la fuente en cuestión, se utiliza un diccionario es-
tático. Por ejemplo, si se desea comprimir los datos de los estudiantes de una
universidad, hay palabras como estudiante, nombre, créditos, etc que serán
muy frecuentes. Por el contrario, si no se dispone de ese conocimiento previo,
tendremos que adquirirlo, de algún modo, en el momento de la codificación.
Esta es la idea clave para la elaboración de un diccionario dinámico.

4.1. Diccionarios estáticos


Una de las formas más comunes de crear un diccionario estático es la sigu-
iente. El diccionario consta de todas las letras del alfabeto fuente junto con las
cadenas de 2 sı́mbolos (llamadas digrams) más frecuentes, hasta completar

57
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 58

el tamaño escogido para éste. Por tanto, se precisa un conocimiento previo


acerca del tipo de datos. Si se trata, por ejemplo, de texto en castellano,
podemos analizar varias páginas al azar de algún libro para determinar las
frecuencias con que aparecen los distintos pares de sı́mbolos, incluido el es-
pacio, y escoger los pares más frecuentes para formar el diccionario junto con
los propios sı́mbolos individuales. Ası́, si se desea elaborar un diccionario de
tamaño 256 para codificar los 95 caracteres imprimibles ASCII, las primeras
95 entradas del diccionario serı́an los caracteres mencionados y las restantes
161 entradas serı́an las parejas más frecuentes. Cada entrada de nuestro dic-
cionario se codifica con una cadena binaria de 8 bits.
El codificador lee el primer par de sı́mbolos y trata de ver si está en
el diccionario. Si lo está, codifica el par con la palabra-código que le corre-
sponde. Si, por el contrario, no lo está, codifica el primer sı́mbolo del par
con la palabra-código correspondiente y pasa a considerar un nuevo par, que
estarı́a formado por la segunda componente del par anterior y el siguiente
sı́mbolo en el mensaje. Como todas las palabras-código son de igual longitud
la decodificación no ofrece problema alguno.

Ejemplo 4.1.1. Supongamos que S = {a, b, c, d, e} es el alfabeto fuente y que


sabemos que las parejas de sı́mbolos más frecuentes son : ab, ac, ad. Podemos
elaborar un diccionario de tamaño 8 como el que se indica a continuación

Entrada Palabra-código
a 000
b 001
c 010
d 011
e 100
ab 101
ac 110
ad 111
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 59

En el código ASCII de 7 bits el mensaje sin comprimir consta de 35 bits


y comprimido de esta forma, 9.

4.2. Diccionarios dinámicos


La mayorı́a de las técnicas de compresión de datos que se basan en dic-
cionarios dinámicos tienen su raı́z en los trabajos de J. Ziv y A. Lempel,
miembros del Departamento de Ingenierı́a Eléctrica en Technion (Haifa). En
1977 publicaron el artı́culo “A universal algorithm for sequential data com-
pression”, en la revista IEEE Transactions on Information Theory, y un año
después en la misma revista el que lleva por tı́tulo “Compression of individual
sequences via variable-rate coding”. Estos trabajos proporcionan dos modos
diferentes de construir diccionarios dinámicos y, de hecho, a partir de cada
una de ellas se han originado diversas variaciones de la idea original. Las que
se basan en el trabajo de 1977 se suele decir que son técnicas de compresión
del tipo LZ77 y las basadas en el de 1978 se llaman técnicas del tipo LZ78.
A continuación desarrollamos el algoritmo original LZ77 junto con algunas
de las modificaciones introducidas por la variante conocida por LZSS. En
cuanto a las técnicas del tipo LZ78, estudiaremos el método LZW que es una
variante, debida a T. Welch, del algoritmo considerado por Ziv y Lempel en
su trabajo de 1978.

4.3. El método LZ77


El algoritmo precisa de una ventana deslizante que contiene n posiciones,
dividida en dos partes: una primera llamada diccionario y una segunda lla-
mada buffer (look-ahead buffer) con un número de posiciones igual a n − c y
c, respectivamente.



Diccionario y look-ahead buffer
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 60

··· a ···

Se inicia el algoritmo con el diccionario y el buffer llenos con los primeros n


sı́mbolos del mensaje fuente. Los sı́mbolos que están en el diccionario en este
momento inicial se codifican tal cual. Para facilitar la explicación, suponemos
que el primer sı́mbolo en el buffer es “a”, como se indica en la figura anterior.
El método consiste en localizar “a” en el diccionario y, si la hay, se escoge
aquella que tiene la propiedad de que un trozo del diccionario que comienza
con dicha “a” coincide con un trozo del buffer que comienza en “a”. Caso
de haber varias posibilidades, se escoge la del trozo de mayor longitud (se
denomina en inglés match). Entonces el algoritmo crea una terna ho, `, si. o
recibe el nombre de offset e indica la localización de “a” en el diccionario,
` es la longitud del match encontrado y s es el primer sı́mbolo en el buffer
que sigue al match. El offset puede medirse contando a partir de la izquierda
o de la derecha y será algo que habrá de decidirse en el momento de usar
el algoritmo. Otra cuestión que hay que señalar es el hecho de que el match
puede prolongarse dentro del buffer, para de esta forma conseguir un match
de mayor longitud. Si se da el caso de que el primer sı́mbolo del buffer, “a”,
no se encuentra en el diccionario, se genera una terna del tipo h0, 0, ai. En
este momento la cadena comprimida consta de los sı́mbolos que componı́an
el diccionario inicial seguida de la terna creada. Una vez que se ha creado la
terna, se actualizan el diccionario y el buffer, dando entrada en el diccionario
a la cadena del buffer que coincide con el match más el sı́mbolo siguiente
(cuando no lo hay, se da entrada en el diccionario al sı́mbolo “a”).

Ejemplo 4.3.1. Supongamos que se desea aplicar el método LZ77 para com-
primir el mensaje

abbacadabbaacaacaccaadca.
Vamos a considerar una ventana deslizante de 13 posiciones y un dic-
cionario de 7. La forma del diccionario y del buffer al principio es la siguiente
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 61



Diccionario → y look-ahead buffer

a b b a c a d a b b a a c ←−

Para determinar el primer match, buscamos en el diccionario la cadena


más larga que comienza por “a” y coincide con un trozo inicial del buffer.
Vemos que se trata de “a b b a” y, por tanto, se forma la terna h7, 4, ai (para
determinar el offset, contamos de derecha a izquierda en el diccionario). La
ventana actualizada toma la forma



Diccionario → y look-ahead buffer

a d a b b a a c a a c a c ←−

Ahora el buffer comienza por “c”, que no está en el diccionario. Entonces


el algoritmo genera la terna h0, 0, ci y se actualiza la ventana dando entrada
a “c” en el diccionario (por la derecha), adoptando la forma



Diccionario → y look-ahead buffer

d a b b a a c a a c a c c ←−

Con un offset igual a 3, localizamos un match de longitud 4 que se prolonga


en el buffer: “a a c a”. Se genera, por tanto, la terna h3, 4, ci y se procede a
actualizar la ventana pasando “a a c a c” al diccionario.



Diccionario → y look-ahead buffer

a c a a c a c c a a d c a ←−
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 62

Ahora el match es “c a a”, que permite generar la terna h6, 3, di. El resul-
tado hasta ahora serı́a la cadena

abbacadh7, 4, aih0, 0, cih3, 4, cih6, 3, di.


Este es el momento de ver cómo se podrı́a codificar la cadena “ comprimi-
da” anterior. Una vez establecidos los tamaños del diccionario y del buffer, se
pueden codificar en binario el offset y la longitud de una terna con un número
determinado de bits cada uno que denotaremos por k y h respectivamente
(en nuestro ejemplo, k = h = 3). Los sı́mbolos que anteceden a las ternas en
ASCII de nivel 7 se codifican con cadenas de 7 bits. Como el decodificador
conoce la longitud del diccionario, puede determinar dónde comienzan los
bits correspondientes a las ternas y, a partir de ese punto, sabe que cada
grupo de k + h + 7 bits representa la codificación de una terna.
En el caso del ejemplo, una terna se codifica con 13 bits, por tanto, hay
compresión para las ternas cuya longitud ` sea mayor que 2. Por el contrario,
una terna del tipo h0, 0, ci requiere 13 bits. Este es uno de los mayores incon-
venientes de este sistema. Otro problema a tener presente es el del tiempo
que se invierte en generar las ternas. En general, la determinación de cada
match requiere hacer (n − c) · c comparaciones. Por ello, aunque es cierto
que aumentando las longitudes del buffer y del diccionario se consigue may-
or compresión, es obvio que el tiempo de compresión aumenta. Para aliviar
estos problemas se han introducido posteriormente algunas modificaciones
al método original. Terminamos esta sección considerando una modificación
introducida en 1982 por Storer y Szymanski que dan origen al método de-
nominado LZSS. La idea fundamental consiste en usar un bit adicional para
anteponer a las ternas y a los sı́mbolos, y emplear parejas en lugar de ternas.
Se antepone un 1 a cada sı́mbolo y un 0 a cada pareja, formada por el offset
y la longitud del match correspondiente.
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 63

4.4. El método LZW


Como hemos dicho con anterioridad, es una variante del método LZ78
original debida a T. Welch y es uno de los algoritmos de compresión más
usados.
El diccionario debe ser creado tanto por el codificador como por el decod-
ificador. Se inicia el método con un diccionario que contiene como entradas
todas las letras del alfabeto fuente. Para facilitar la explicación, supongamos
que el alfabeto fuente es S = {&, a, b, c, v} y que deseamos codificar el men-
saje
vbaab&vbaab&.
Inicialmente, el diccionario tiene la forma

ı́ndice entrada
1 &
2 b
3 a
4 c
5 v

El primer sı́mbolo que se recibe es v, que está en el diccionario. Se procede a


concatenarlo con el siguiente y resulta la cadena vb.
Como no pertenece a la lista, se codifica v con el ı́ndice que le corresponde
en el diccionario (5) y se añade a éste la cadena vb como sexta entrada. Pros-
eguimos con la codificación considerando la letra que sigue a la que acabamos
de codificar, es decir, b. Como está en el diccionario, se concatena con el
siguiente sı́mbolo y resulta la cadena ba. Como no pertenece al diccionario,
codificamos b con su ı́ndice correspondiente (2) y se añade a la lista ba como
séptima entrada.
Continuamos de esta forma con la codificación y supongamos que se ha
llegado hasta la segunda v ( concretamente, acabamos de añadir al diccionario
la cadena &v). En este momento, éste tiene la forma siguiente:
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 64

ı́ndice entrada
1 &
2 b
3 a
4 c
5 v
6 vb
7 ba
8 aa
9 ab
10 b&
11 &v

Llegados a este punto, procederı́amos a codificar & con su ı́ndice (1), para
continuar con la codificación de v. Como v está en el diccionario, se concatena
v con el siguiente sı́mbolo, lo que produce la cadena vb. Como está en el
diccionario, se concatena con la siguiente letra del mensaje y resulta vba. Al
no estar en la lista, la añadimos como la entrada número 12, codificamos vb
con su ı́ndice (6) y se continua el proceso. La porción del mensaje que hemos
codificado adopta la forma: 5233216810. Esto supone que el mensaje fuente
ha sido “troceado” de la forma siguiente:

v, b, a, a, b, &, vb, aa, b&.

Una forma de recoger ordenadamente todos los pasos que se van dando
consiste en elaborar una tabla de 5 columnas. En la primera se colocan lo
que llamaremos prefijos, en la segunda los sufijos, en la tercera nueva cadena,
en la cuarta los ı́ndices, que se corresponden con las entradas del diccionario
que vamos codificando (el sı́mbolo “-” indica que nueva cadena está en el
diccionario) y, finalmente, en la quinta las nuevas entradas del diccionario.
Los sufijos son los sı́mbolos del mensaje a medida que van siendo recibidos
por el codificador. El prefijo inicial es la cadena vacı́a y será denotado por
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 65

∆. Cada nueva cadena se forma concatenando prefijo y sufijo. Las filas de la


tabla se van formando de acuerdo con las siguientes reglas:
1) Si nueva cadena está en el diccionario, ponemos el sı́mbolo “-” en las
columnas cuarta y quinta para indicar que no hay salida del codificador y
que el diccionario no sufre variación. Además, nueva cadena pasa a ser el
siguiente prefijo y se da entrada al siguiente sı́mbolo del mensaje que pasa a
ser el sufijo actual.
2) Si nueva cadena no está en el diccionario, se procede a añadirla al
diccionario como una nueva entrada, codificamos el prefijo con el ı́ndice que
le corresponde y lo anotamos en la cuarta columna y, por último, el sufijo
pasa a ser el prefijo siguiente y se da entrada al nuevo sı́mbolo del mensaje.
En nuestro ejemplo, la tabla tiene la forma siguiente

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ v v - -
2 v b vb 5 vb(6)
3 b a ba 2 ba(7)
4 a a aa 3 aa(8)
5 a b ab 3 ab(9)
6 b & b& 2 b&(10)
7 & v &v 1 & v(11)
8 v b vb - -
9 vb a vba 6 vba(12)
10 a a aa - -
11 aa b aab 8 aab(13)
12 b & b& - -
13 b& ∆ b& 10 -

Por su parte, la decodificación consiste en ir reproduciendo los pasos que


hemos ido dando durante la codificación (recordemos que el diccionario ini-
cial es el mismo en los dos procesos). El mensaje a decodificar comienza por
5 y vemos que está en el diccionario y se corresponde con la letra v. Entonces
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 66

el decodificador decide que la primera letra del mensaje es v y pasa a consid-


erar el siguiente número que es 2. Le corresponde la entrada b, de modo que
el decodificador ya conoce las dos primeras letras del mensaje: vb. Concate-
nando con la anterior, obtiene vb. Como el decodificador debe ir formando
el diccionario de manera análoga a como lo hace el codificador, añade a su
diccionario la cadena vb como sexta entrada y pasa a considerar el siguiente
número (3). Le corresponde la entrada a que está en el diccionario. Entonces
la concatena con la letra que se acaba de decodificar, b, y resulta ba. La añade
a la lista como entrada número 7 y decodifica 3 como a.
Esto puede hacerse, al igual que en el caso de la compresión, mediante una
tabla de 5 columnas. Se trata de ir reconstruyendo la tabla anterior. Veamos
cómo puede hacer esto el decodificador con los únicos datos del diccionario
inicial y el mensaje codificado. La primera fila se reconstruye de forma ob-
via, dando entrada al primer elemento del mensaje comprimido que puede
decodificarse como “v”.
prefijo sufijo nueva cadena ı́ndice nueva entrada
1 ∆ v v - -
El siguiente elemento que recibe el decodificador es 2 y como está en el
diccionario puede ser decodificado como “b”. Por tanto, “v” es el prefijo en
la segunda fila y “b” el sufijo:
prefijo sufijo nueva cadena ı́ndice nueva entrada
1 ∆ v v - -
2 v b vb 5 vb(6)
Abordamos la tercera fila, de la que conocemos el prefijo que debe ser
“b” (sufijo anterior). El decodificador lee el tercer elemento del mensaje com-
primido y, como se trata de 3, puede decodificarlo como “a”. Por tanto, la
fila tercera ya puede completarse
prefijo sufijo nueva cadena ı́ndice nueva entrada
1 ∆ v v - -
2 v b vb 5 vb(6)
3 b a ba 2 ba(7)
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 67

Las 3 filas siguientes se reconstruyen de igual forma, obteniéndose

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ v v - -
2 v b vb 5 vb(6)
3 b a ba 2 ba(7)
4 a a aa 3 aa(8)
5 a b ab 3 ab(9)
6 b & b& 2 b&(10)

Pasamos ahora a reconstruir la fila 7. Sabemos que el prefijo es “&” (sufi-


jo anterior) y damos lectura al siguiente elemento del mensaje codificado. Se
trata del ı́ndice 6, al que corresponde en el diccionario la cadena “vb”. En-
tonces los sufijos de las filas 7 y 8 son “v” y “b”, respectivamente (recuérdese
que el mensaje fuente va apareciendo en la columna de sufijos.

prefijo sufijo nueva cadena ı́ndice nueva entrada


7 & v &v 1 & v(11)
8 b

El prefijo de la fila 8 es “v” y ya puede completarse

prefijo sufijo nueva cadena ı́ndice nueva entrada


7 & v &v 1 & v(11)
8 v b vb - -

De la fila 9 conocemos el prefijo, “vb” (nueva cadena anterior), y para


determinar el sufijo damos lectura al siguiente elemento del mensaje codifi-
cado. Ahora es 8, al que corresponde la cadena “aa”. Su primer sı́mbolo es el
nuevo sufijo, luego

prefijo sufijo nueva cadena ı́ndice nueva entrada


8 v b vb - -
9 vb a vba 6 vba(12)
10 a
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 68

El prefijo de la fila 10 es “a” y puede reconstruirse sin problemas

prefijo sufijo nueva cadena ı́ndice nueva entrada


9 vb a vba 6 vba(12)
10 a a aa - -

La fila 11 tiene “aa” (nueva cadena anterior) por prefijo y el sufijo es el


primer sı́mbolo del último elemento recibido. En nuestro caso, es 10, al que
corresponde la cadena “b&”. Por tanto

prefijo sufijo nueva cadena ı́ndice nueva entrada


10 a a aa - -
11 aa b aab 8 aab(13)
12 &

El prefijo de la fila 12 es “b”, luego

prefijo sufijo nueva cadena ı́ndice nueva entrada


10 a a aa - -
11 aa b aab 8 aab(13)
12 b & b& - -

Llegamos, finalmente, a la última fila. No hay sufijo y, por ello, ponemos en


la casilla correspondiente ∆, mientras que el prefijo es “b&” (nueva cadena
anterior).

prefijo sufijo nueva cadena ı́ndice nueva entrada


13 b& ∆ b& 10 -

El mensaje fuente aparece en la columna de los sufijos.


A primera vista puede parecer que el proceso de decodificación no presenta
inconveniente alguno. Sin embargo, vamos a ver que pueden darse situaciones
en las que, para determinar el sufijo de una cierta fila, se precise conocer
una entrada que, para el decodificador, aún no está en el diccionario. En
concreto, este inconveniente menor se presenta cada vez que, en el mensaje
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 69

que sale del compresor, hay dos ı́ndices consecutivos cuyas correspondientes
entradas son del tipo C y Cs, donde s denota un sı́mbolo individual y C
es una cadena. Afortunadamente, como veremos en seguida. esta dificultad
puede remediarse.
Veamos un sencillo ejemplo en el que se da esta situación.

Ejemplo 4.4.1. Consideramos el alfabeto fuente S = {a, b} y el mensaje


siguiente: “a b a b a b a b”. El desarrollo del algoritmo de codificación se
recoge en la tabla siguiente

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ a a - -
2 a b ab 1 ab(3)
3 b a ba 2 ba(4)
4 a b ab - -
5 ab a aba 3 aba(5)
6 a b ab - -
7 ab a aba - -
8 aba b abab 5 abab(6)

Tratemos de decodificar la sucesión obtenida: 1 2 3 5.... El decodificador


comienza con el mismo diccionario inicial
ı́ndice entrada
1 a
2 b

Las primeras dos filas se reconstruyen sin problemas

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ a a - -
2 a b ab 1 ab(3)
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 70

Ahora el decodificador da entrada al tercer ı́ndice del mensaje codificado


que es un 3. En su diccionario ya está la entrada 3; se trata de “ab” y, por
tanto, el decodificador sabe que los siguientes sı́mbolos del mensaje son “a”
y “b”. La siguiente fila tiene la forma

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ a a - -
2 a b ab 1 ab(3)
3 b a ba 2 ba(4)
4 b

La fila 4 se puede reconstruir sin problemas. Ya conoce el sufijo y el prefijo


es el sufijo de la fila anterior

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ a a - -
2 a b ab 1 ab(3)
3 b a ba 2 ba(4)
4 a b ab - -

Sabemos que “ab” es el prefijo en la fila 5 y, para decidir el sufijo, damos


entrada al siguiente elemento del mensaje codificado, 5 en nuestro caso. Sin
embargo, para el decodificador, el diccionario en el momento actual sólo tiene
cuatro entradas. El sufijo en la fila 5 serı́a el primer sı́mbolo de la quinta en-
trada, pero ésta aun no la conoce el decodificador, pero sı́ sabe que debe
comenzar con “ab”. Cuando se presenta este problema, la solución es la sigu-
iente. La entrada con ı́ndice 5 debe comenzar con el prefijo de la fila sexta y
éste, a su vez, es el sufijo de la quinta. Esto obliga a que necesariamente el
sı́mbolo final de la entrada 5 sea igual al primero, es decir, “a”.
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 71

prefijo sufijo nueva cadena ı́ndice nueva entrada


1 ∆ a a - -
2 a b ab 1 ab(3)
3 b a ba 2 ba(4)
4 a b ab - -
5 ab a aba 3 aba(5)

En la práctica, lo usual es fijar el tamaño del diccionario y en el momento


que éste se alcance el algoritmo funciona como en el caso de un diccionario
estático. Ası́, si el tamaño es 2m , los ı́ndices se codifican en binario con m
bits. En el apartado siguiente se consideran algunos sistemas comerciales que
usan esta técnica.

4.5. Aplicaciones
El comando compress de UNIX es una de las primeras aplicaciones del
método LZW. Comienza con un diccionario de tamaño 512, por lo que las
palabras-código constan de 9 bits. Cuando el diccionario se completa, se
duplica su tamaño. Las 1024 entradas se codifican ahora con 10 bits. El
tamaño se va duplicando progresivamente, a medida que se va completando.
El usuario puede fijar la longitud máxima de las palabras-código, que será
siempre un número comprendido entre 9 y 18 (16 es el tamaño máximo por
defecto). Una vez que se alcanza el tamaño máximo, compress se convierte
en una técnica de compresión con diccionario estático.
También se usa el método LZW en la compresión de imágenes. El formato
GIF (Graphics Interchange Format) fue desarrollado por CIS (Compuserve
Information Service) para codificar imágenes gráficas. Usa el algoritmo LZW
y se parece bastante al comando compress de UNIX. La imagen comprimida
se almacena comenzando con un byte que representa el mı́nimo número m
de bits por pixel en la imagen original y el tamaño inicial del diccionario
es 2m+1 y, como en el caso anterior, se va duplicando a medida que se va
completando. Cuando el tamaño es de 4096, el algoritmo pasa a convertirse
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 72

en un método de diccionario estático.


Finalmente, señalar que el método LZW se usa en los modems que conectan
ordenadores con un usuario remoto. ITU-T tiene una recomendación llamada
V.42 bis que usa un algoritmo que opera en dos modos, uno sin comprimir y
otro con compresión. En este último el método usado es el LZW.

4.6. Códigos de Tunstall


Terminamos el capı́tulo estudiando un código usado en la compresión de
datos que presenta la particularidad de que las palabras-código son de igual
longitud. Su principal ventaja radica en el hecho de que los errores en las
palabras-código no se propagan, lo que sı́ ocurre en el código de Huffman. En
temas anteriores hemos estudiado métodos de compresión apropiados para el
caso de que los sı́mbolos fuente se producen con frecuencias relativas signi-
ficativamente diferentes y se conocen dichas frecuencias. Estos métodos cod-
ificaban los sı́mbolos más frecuentes con palabras-código de menor longitud.
El código de Tunstall va a adoptar un punto de vista diferente. Dados el al-
fabeto fuente S = {a1 , .., an } y el conjunto de probabilidades P = {p1 , .., pn },
el código de Tunstall usa palabras-código de igual longitud para codificar una
lista de palabras fuente.
Lógicamente, el código de Tunstall trata de maximizar el número medio
de sı́mbolos fuente representados por cada palabra-código.
Para facilitar su comprensión vamos a considerar, en primer lugar, un
ejemplo.

Ejemplo 4.6.1. Sea S = {a, b, c} el alfabeto fuente y P = {0.6, 0.3, 0.1}


el conjunto de probabilidades. Construir el código de Tunstall con palabras-
código de longitud 3.
El primer paso consiste en seleccionar la letra del alfabeto fuente con may-
or probabilidad. En nuestro caso, es la letra “a”. Seguidamente, se considera
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 73

el conjunto formado por las cadenas de longitud 2 de la forma “a-”, junto con
las de longitud 1 distintas de “a”.
La tabla muestra cada una de estas cadenas junto con las probabilidades
correspondientes.

cadenas probabilidades
b 0.3
c 0.1
aa 0.36
ab 0.18
ac 0.06

Ahora se selecciona la cadena con mayor probabilidad de la tabla anterior.


Se trata de la palabra “aa” cuya probabilidad es 0.36. Creamos un segundo
conjunto de cadenas eliminando en el anterior la cadena “aa” y añadiendo
todas las palabras de longitud 3 de la forma “aa-”:

D = {b, c, ab, ac, aaa, aab, aac}.

El conjunto D resultante tiene 7 elementos por lo que puede codificarse (en


binario) con palabras-código de longitud 3.

Para abordar un caso genérico, supongamos que el alfabeto fuente S tiene


n sı́mbolos y deseamos determinar el código de Tunstall con palabras-código
de longitud L. El número máximo de palabras-código es igual a 2L . Inicial-
mente, la tabla tiene la forma

cadenas probabilidades
a1 p1
a2 p2
... ...
an pn

Por tanto, tenemos n cadenas. En el primer paso añadimos n cadenas de


longitud 2 y eliminamos una de longitud 1. Es decir, tras el primer paso la
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 74

tabla contiene n + (n − 1) cadenas. Nótese que en cada paso ocurre lo mismo:


se añaden n − 1 nuevas cadenas. Luego, después de aplicar k veces el proceso,
tendremos una tabla provisional con n+k·(n−1) cadenas. El método termina
cuando k verifica las desigualdades

n + k · (n − 1) ≤ 2L y n + (k + 1) · (n − 1) > 2L .

Una vez que se ha creado la tabla, el compresor comienza a subdividir


el mensaje fuente en cadenas que figuran en dicha tabla y las sustituye por
las palabras-código correspondientes. Hay que señalar que puede ocurrir que
un trozo final del mensaje no pueda codificarse porque no se encuentre en
la tabla. Veamos un caso concreto en relación con el ejemplo anterior. Con-
sideramos el mensaje fuente “baba”. El compresor codifica b con su código
correspondiente y seguidamente pasa a codificar ab. Pero el sı́mbolo final a
no está en la tabla y no podemos codificarlo. Vamos a ver una forma de re-
solver este problema. Se escoge el tamaño del diccionario de modo que quede
sin adjudicar alguna cadena binaria de longitud L. Entonces, cuando llegue
el turno de codificar el trozo problemático final, usamos una de las cade-
nas binarias no adjudicadas seguida por la codificación (sin comprimir) que
corresponda a los sı́mbolos de que consta el trozo final.
Finalizamos esta sección considerando el problema de determinar el grado
de eficacia del código de Tunstall. Para ello, debemos relacionar el número
medio de bits necesarios para codificar un sı́mbolo fuente con la entropı́a
H(S). Para fijar ideas, supongamos que estamos considerando un código de
Tunstall de L bits, es decir, cada cadena de la tabla se codifica con L bits.
Si m denota el número de entradas de la tabla, `i la longitud de la en-
trada i-ésima y Pi la probabilidad de dicha entrada (igual al producto de las
probabilidades de los sı́mbolos de que consta), entonces el número medio de
bits por sı́mbolo será
L
`= m .
X
Pi `1
i
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 75

Si D denota el conjunto formado por las m cadenas que pertenecen a la lista,


existe la siguiente relación entre las entropı́as de S y D:
m
¡X ¢
H(D) = H(S) · Pi `i (4.1)
i=1

Por otra parte, no es difı́cil probar la desigualdad


¡
Pi ≤ mpmin )−1 ,

donde pmin = min{pi : 1 ≤ i ≤ n} (la desigualdad es realmente interesante


cuando mpmin > 1). Entonces
m
X m
¡ ¢ X
H(D) = Pi log2 1/Pi ≥ Pi log2 (mpmin ) =
i=1 i=1

= log2 (mpmin ).
Con esta desigualdad y (4.1) obtenemos

L LH(S) LH(S)
`= m = ≤ .
X H(D) log2 (mpmin )
Pi `1
i

Por último, como 2L−1 ≤ m < 2L , se sigue que L − 1 ≤ log2 m. Por tanto,
obtenemos para ` la siguiente acotación
1 + log2 m
` ≤ H(S) .
log2 (pmin ) + log2 m

El factor que multiplica a H(S) en el segundo miembro de la última desigual-


dad es mayor que 1 y , cuando m → ∞, tiende a 1. Esto prueba que usando un
código de Tunstall con un número m de palabras convenientemente grande,
el número medio de bits por sı́mbolo fuente, `, estará próximo a la entropı́a
de la fuente S.
En el caso del primer ejemplo, n = 3, m = 7 y las longitudes se recogen
en la siguiente tabla junto con las probabilidades correspondientes
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 76

cadenas longitudes probabilidades


b 1 10.3
c 1 0.1
aa 2 0.36
ab 2 0.18
ac 2 0.06
aaa 3 0.216
aab 3 0.108
aac 3 0.036
Con un sencillo cálculo encontramos ` = 3/1.96 = 1.5306. Por otra
parte, la entropı́a de la fuente es igual a 1.2955 y el código de Huffman
C = {1, 01, 00}, cuya longitud media es 1.4. Por tanto, con este ejemplo
vemos que el código de Tunstall no es óptimo pero sı́ está muy próximo a
serlo.

4.7. Ejercicios
1. Se considera el alfabeto fuente S = {a, b, c} con probabilidades P =
{0.7, 0.2, 0.1}. a) Determinar la entropı́a de la fuente y el código de Tun-
stall de 3 bits. b) Obtener el código de Huffman y comparar los resultados
obtenidos.

2. Se considera el alfabeto fuente S = {a, b, c, d} y se desea codificar el men-


saje “a b a b b a c” por el método LZW. Sol.1 2 5 6 3.

3. Con los datos del ejercicio anterior, decodificar la cadena 1 3 6 5 3. Sol. a


c c c a c c.

4.8. Prácticas de Programación


1. Elaborar un programa de Matlab para codificar y decodificar un men-
saje de longitud arbitraria.
CAPÍTULO 4. TÉCNICAS DE DICCIONARIO 77

a) con un código de Tunstall


b) con un diccionario estático
c) con el algoritmo LZ77.
d) con el algoritmo LZW

También podría gustarte