![]() |
LABORATORIO DE ESTRUCTURAS DE DATOS PRÁCTICAS CURSO 1996-97 1º INGENIERÍA INFORMÁTICA |
![]() |
(Semanas del 26 de Mayo al 6 de Junio de 1997)
Supóngase que se tienen mensajes formados por secuencias de caracteres (un texto cualquiera tendría esta forma). En cada mensaje, los caracteres son independientes entre sí y aparecen con una determinada probabilidad conocida a priori. Se desea codificar cada carácter mediante una secuencia de ceros y unos, de manera que ningún código de un carácter sea prefijo del código de otro carácter, esta propiedad de prefijos evitará cualquier tipo de ambigüedad en el proceso de decodificación del mensaje.
El problema de codificación que se plantea sería: dado un conjunto de caracteres, en nuestro caso supongamos que todo el código ASCII (256 valores), y sus probabilidades de aparición en un texto (este dato no es conocido a priori y tendremos que hacer un estudio previo), encontrar un código para esos caracteres que cumpla la propiedad de prefijos y tal que la longitud media del código de un carácter sea mínima. La razón por la que se desea minimizar la longitud media de un código es que ello permitirá comprimir la longitud del mensaje codificado.
El algoritmo de Huffman permite encontrar un código con las propiedades especificadas. La idea del algoritmo consiste en llegar a formar un árbol binario que represente el código. En los nodos terminales (hojas) de este árbol se almacenarán los caracteres que se desea codificar junto con la probabilidad de aparición de cada uno de ellos, cumpliéndose que los caracteres más probables están más próximos a la raíz que los menos probables. De manera que el camino desde la raíz del árbol a cualquier hoja del mismo representará el código para el carácter de esa hoja, de acuerdo con el criterio de que los subárboles izquierdos se etiquetan con un 0 y los subárboles derechos lo hacen con un 1. Según este criterio, los caracteres con mayor probabilidad de aparición tendrán los códigos de menor longitud, mientras que los que tengan una probabilidad de aparición menor tendrán asociado un código de mayor longitud.
El algoritmo de Huffman consiste en lo siguiente:
Suponiendo conocida la probabilidad de aparición de un carácter en un texto (sea pi) hacer:
Para este proceso se incluirá en el programa un rutina que calcule el número de veces que aparece cada carácter en el fichero texto. Será necesario definir una tabla que almacene la frecuencia de aparición de cada carácter.
Esta tarea se realizará después de la tarea 1 y su esquema será:
En esta tarea, a partir del fichero texto original, se obtendrá un fichero "binario" (sólo ceros y unos, como caracteres (1 byte), no como bits) que haga corresponder a cada carácter del fichero original el código de Huffman correspondiente.
La rutina correspondiente a esta tarea responderá al siguiente esquema:
En esta fase habrá que estimar cuanto ocuparía el fichero binario si cada `0' y `1' escrito en él fuese un bit, en vez de un carácter. ¿Se conseguiría comprimir el fichero original? ¿Y si consideramos lo que ocupa la cabecera con el árbol de códigos?
Es conveniente, una vez realizada la codificación, liberar el espacio reservado para el árbol, ya que no vamos a utilizarlo más en el programa.
El esquema de la rutina será el siguiente:
Los procesos de Codificación y Decodificación se englobarán en un mismo programa que permita ejecutar la tarea deseada. Se deja a criterio del alumno la conveniencia de realizar un diseño del programa en forma de módulos independientes, utilizando Unidades o ficheros Include.
Para realizar convenientemente todas las operaciones con árboles binarios, será necesario definir un tipo de datos ARBOL (que se implementará mediante una representación enlazada), con un conjunto de operaciones básicas asociadas. Éstas serán las siguientes:
Cualquier operación de manipulación de árboles que haya que realizar sobre árboles binarios en el programa se debe basar exclusivamente en estas operaciones básicas.
CONST Primero = 0; Ultimo = 255; {Estas dos constantes definen el rango del código ASCII que se va a considerar. Si las rutinas las escribimos con referencia a estas constantes, el programa se podrá adaptar fácilmente a otro conjunto de caracteres sin más que modificar estos valores} TYPE Posicion = Primero..Ultimo; {Establece el rango de caracteres válidos} {Definición de la tabla de frecuencias de aparición de caracteres} Frecuencia = 0..MaxInt; {Para indicar el n1 de veces que aparece un carácter} Tabla_Frec = array [ Posicion ] of Frecuencia; {Definición de la matriz que almacenará el código generado por el método de Huffman} Tabla_Codigos = array [ Posicion ] of String; { Las cadenas contendrán solo '0's o '1's } {Definición del tipo árbol} {Un nodo del árbol constará de la siguiente información: Frec : Indica la frecuencia de aparición de los caracteres que forman el árbol del cual el nodo es raíz. simbolo : Indica si el nodo almacena un carácter o está formado por la agrupación de caracteres. Sólo los nodos terminales tendrán este campo puesto a true. En ese caso, se define un campo más, info que almacena el carácter. } Valor = Record Frec : Frecuencia; case simbolo : boolean of true: (info: char); false: (); end; Arbol = ^Nodo; Nodo = Record Dato : Valor; Sub_Izq, Sub_Der : Arbol; end; {Un bosque será una agrupación de árboles, tantos como posibles caracteres tengamos} Bosque = array [ Posicion ] of Arbol; {Tipos fichero que se utilizarán para manipular archivos de disco} Ficheros = file of char; {No se utiliza text porque sólo interesan los caracteres individualmente, se usarán tanto para los ficheros codificados como para los de texto}ENTREGA DE PROGRAMAS: Al finalizar la segunda sesión de prácticas.