English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

invitado
1 / ?

Cómo Huffman Construye el Código Óptimo

Hamming presenta la codificación de Huffman como un algoritmo codicioso que construye un código libre de prefijos con longitud promedio mínima. La idea clave: construir el árbol de abajo hacia arriba, fusionando siempre los dos símbolos menos probables.

El Paso Adelante (Construir)

1. Ordenar todos los símbolos por probabilidad, de mayor a menor.

2. Tomar los dos símbolos de menor probabilidad. Combinarlos en un nuevo metasímbolo con probabilidad = suma de los dos.

3. Insertar el metasímbolo en su posición ordenada.

4. Repetir hasta que solo queden dos símbolos.

5. Asignar 0 y 1 a los dos símbolos restantes.

El Paso Hacia Atrás (Asignar Códigos)

Deshacer las fusiones en reversa: en cada división, los dos símbolos que fueron fusionados reciben los mismos bits de prefijo que su padre combinado, más un 0 adicional (un hijo) o 1 (otro hijo). La asignación de 0/1 es arbitraria — cualquiera es válido.

Huffman Build: Merge and Decode Tree

Por qué esto es óptimo: si cualquier otro código tuviera una longitud promedio menor L', aplicando la misma reducción hacia adelante eventualmente produciría dos símbolos con longitud promedio menor a 1 bit — imposible para un código de 2 símbolos. Contradicción.

Rastreando el Algoritmo

Ejemplo de Hamming: cuatro símbolos A, B, C, D con p(A)=0.5, p(B)=0.25, p(C)=0.125, p(D)=0.125.

Paso adelante:

Paso 1: Ordenado: A(0.5), B(0.25), C(0.125), D(0.125). Los dos más bajos: C, D.

Paso 2: Fusionar CD con p=0.25. Nueva lista: A(0.5), B(0.25), CD(0.25).

Paso 3: Los dos más bajos: B(0.25), CD(0.25). Fusionar BCD con p=0.5.

Paso 4: Dos símbolos restantes: A(0.5), BCD(0.5). Asignar A=0, BCD=1.

Paso hacia atrás:

BCD → B obtiene 10, CD obtiene 11. CD → C obtiene 110, D obtiene 111.

Código final: A=0 (l=1), B=10 (l=2), C=110 (l=3), D=111 (l=3).

L = 0.5·1 + 0.25·2 + 0.125·3 + 0.125·3 = 1.75 bits.

Aplica el algoritmo de Huffman a: p(X1)=0.4, p(X2)=0.3, p(X3)=0.2, p(X4)=0.1. Muestra todos los pasos de fusión, el código final para cada símbolo, y calcula L.

Múltiples Códigos de Huffman Válidos

Hamming señala dos fuentes de no unicidad:

1. Asignación arbitraria de 0/1. En cada división, puedes asignar 0 a cualquier hijo. Intercambiar 0 y 1 en todas partes da un código diferente con L idéntico.

2. Desempate. Cuando dos nodos tienen probabilidad igual, cualquiera puede colocarse 'más alto' (fusionarse primero). Diferentes opciones de desempate pueden producir árboles estructuralmente diferentes — 'largos' vs 'frondosos' — con el mismo L pero diferentes distribuciones de longitud de código.

Largo vs Frondoso

Árbol largo (sesgado): un símbolo en cada nivel de profundidad (estructura similar a Fibonacci). Las longitudes de código varían ampliamente: un símbolo obtiene un código corto, otros se propagan a códigos más largos.

Árbol frondoso (equilibrado): símbolos distribuidos uniformemente en niveles de profundidad. Las longitudes de código se agrupan cerca del promedio. Menor varianza.

Long vs Bushy Huffman Trees

Recomendación de Hamming: cuando tengas opción, prefiere el árbol frondoso. Mismo L, pero menor varianza en las longitudes de código → tiempo de decodificación más uniforme → menores requisitos de búfer en aplicaciones en tiempo real.

Calculando la Varianza de Longitud de Código

Varianza de las longitudes de código: Var = E[l²] − (E[l])²

Para el código {A=0 l=1, B=10 l=2, C=110 l=3, D=111 l=3} con p=[0.5, 0.25, 0.125, 0.125]:

E[l] = L = 1.75

E[l²] = 0.5·1² + 0.25·2² + 0.125·3² + 0.125·3² = 0.5 + 1.0 + 1.125 + 1.125 = 3.75

Var = 3.75 − 1.75² = 3.75 − 3.0625 = 0.6875

Un código frondoso alternativo para símbolos equiprobables usa todos los códigos de longitud 2: L=2, Var=0.

Considera 4 símbolos equiprobables (p=0.25 cada uno). Calcula H. Luego compara: (a) el código frondoso {00, 01, 10, 11} con todas las longitudes = 2, y (b) un código largo con longitudes {1, 2, 3, 3}. Calcula L y Var para cada uno. ¿Cuál deberías preferir y por qué?

Ganancias de Compresión vs Distribución de Símbolos

Regla de Hamming: la codificación de Huffman se justifica solo cuando las probabilidades de los símbolos difieren sustancialmente.

Probabilidades iguales. Si todos los símbolos 2^k tienen probabilidad igual 1/2^k, Huffman produce un código de bloque: cada símbolo obtiene longitud k. L = H = k. Sin ganancia sobre el código de bloque simple.

Probabilidades sesgadas. Si un símbolo domina (p >> 1/2^k para otros), Huffman le asigna un código muy corto (longitud 1 o 2), reduciendo dramáticamente L.

El código de coma (término de Hamming). Cuando cada probabilidad excede 2/3 del total restante, Huffman naturalmente produce: p(s1)→0, p(s2)→10, p(s3)→110, ..., hasta dos códigos de longitud igual al final. Este es un 'código de coma': el 0 final después de una serie de 1s actúa como una coma.

Hamming observa: la compresión de datos del mundo real (copia de seguridad, archivo de archivos) puede reducir el almacenamiento a más de la mitad cuando la fuente tiene probabilidades sesgadas — el texto en inglés siendo un ejemplo principal.

Huffman vs Código de Bloque: Comparación Numérica

Segundo ejemplo de Hamming: p(s1)=1/3, p(s2)=1/5, p(s3)=1/6, p(s4)=1/10, p(s5)=1/12, p(s6)=1/20, p(s7)=1/30, p(s8)=1/30.

Código de bloque: 8 símbolos → 3 bits cada uno → L_block = 3.

Hamming calcula el código de Huffman e informa L_Huffman ≈ 2.58 bits.

Ahorros: (3 − 2.58)/3 ≈ compresión del 14% sobre codificación de bloque.

Cuando las probabilidades de los símbolos difieren mucho (1/3 vs 1/30 aquí, una proporción de 10:1), la codificación de longitud variable se justifica sustancialmente.

La fuente de 8 símbolos anterior tiene H ≈ 2.55 bits (no necesitas verificar esto). El código de Huffman de Hamming logra L ≈ 2.58 bits. El código de bloque usa L = 3 bits. Calcula: (a) L/H para el código de Huffman, (b) L/H para el código de bloque, y (c) qué te dicen estos cocientes sobre la eficiencia de cada codificación en relación con el mínimo teórico.

Programas Autocompilables

Hamming termina el Capítulo 11 con una idea impactante: un codificador de Huffman puede codificarse a sí mismo. El árbol de decodificación (que le dice al decodificador cómo leer el mensaje comprimido) se transmite junto con los datos comprimidos. El receptor no necesita conocimiento previo del código.

El codificador: muestrea los datos, estima probabilidades, construye el código de Huffman, codifica el árbol de decodificación como encabezado, luego codifica los datos.

El decodificador: lee el encabezado para reconstruir el árbol, luego decodifica los datos usando el mismo.

Punto de Hamming: todo este pipeline puede ejecutarse como una función de biblioteca sin intervención humana. Lo llamas, y comprime y descomprime automáticamente. Los formatos de archivo modernos (ZIP, gzip, bzip2) implementan exactamente este patrón.

El principio más profundo: la inteligencia está en el algoritmo, no en una tabla de códigos fija. El algoritmo se adapta a cualquier dato que reciba. Este es el patrón que Hamming ve en todos los grandes sistemas de ingeniería: adaptabilidad incorporada, no añadida.

Hamming dice que el codificador de Huffman autocompilable 'no requiere interferencia ni pensamiento humano'. ¿Cuál es la virtud de ingeniería de esta propiedad, y qué requiere del diseño del algoritmo? Da un ejemplo concreto de un sistema moderno que encarna este mismo principio.