Codificación canónica de Huffman

Codificación de Huffmanes un algoritmo de compresión de datos sin pérdidas en el que a cada carácter de los datos se le asigna un código de prefijo de longitud variable. El carácter menos frecuente obtiene el código más grande y el más frecuente obtiene el código más pequeño. Codificar los datos usando esta técnica es muy fácil y eficiente. Sin embargo, la decodificación del flujo de bits generado con esta técnica es ineficiente. Los decodificadores (o descompresores) requieren el conocimiento del mecanismo de codificación utilizado para decodificar los datos codificados a los caracteres originales. Por lo tanto, la información sobre el proceso de codificación debe pasarse al decodificador junto con los datos codificados como una tabla de caracteres y sus códigos correspondientes. En la codificación normal de Huffman de datos de gran tamaño, esta tabla ocupa mucho espacio de memoria y también si es un gran no. de caracteres únicos están presentes en los datos, entonces el tamaño de los datos comprimidos (o codificados) aumenta debido a la presencia del libro de códigos. Por lo tanto, para hacer que el proceso de decodificación sea computacionalmente eficiente y aún así mantener una buena relación de compresión, se introdujeron los códigos Canonical Huffman. 
 

En la codificación canónica de Huffman, se utilizan las longitudes de bits de los códigos Huffman estándar generados para cada símbolo. Los símbolos se ordenan primero de acuerdo con su longitud de bits en orden no decreciente y luego, para cada longitud de bit, se ordenan lexicográficamente. El primer símbolo obtiene un código que contiene todos ceros y de la misma longitud que la longitud de bits original. Para los símbolos subsiguientes, si el símbolo tiene una longitud de bits igual a la del símbolo anterior, entonces el código del símbolo anterior se incrementa en uno y se asigna al símbolo actual. De lo contrario, si el símbolo tiene una longitud de bit mayor que la del símbolo anterior, después de incrementar el código del símbolo anterior esse añaden ceros hasta que la longitud llega a ser igual a la longitud en bits del símbolo actual y luego se asigna el código al símbolo actual. 
Este proceso continúa para el resto de los símbolos. 
 

El siguiente ejemplo ilustra el proceso:

Considere los siguientes datos: 

Personaje Frecuencia
a 10
b 1
C 15
d 7

Códigos Huffman estándar generados con longitudes de bit: 

Personaje Códigos Huffman Longitudes de bit
a 11 2
b 100 3
C 0 1
d 101 3

Paso 1: Ordene los datos según la longitud de los bits y luego, para cada longitud de bit, ordene los símbolos lexicográficamente. 

Personaje Longitudes de bit
C 1
a 2
b 3
d 3

Paso 2: Asigne el código del primer símbolo con el mismo número de ‘0’ que la longitud del bit. 
Código para ‘c’: 0
El siguiente símbolo ‘a’ tiene una longitud de bits 2 > longitud de bits del símbolo anterior ‘c’ que es 1. Incremente el código del símbolo anterior en 1 y agregue (2-1) = 1 ceros y asignar el código a ‘a’. 
Código para ‘a’: 10
El siguiente símbolo ‘b’ tiene una longitud en bits de 3 > longitud en bits del símbolo anterior ‘a’, que es 2. Incremente el código del símbolo anterior en 1 y agregue (3-2) = 1 ceros y asigne el código a ‘b’. 
Código para ‘b’: 110
El siguiente símbolo ‘d’ tiene una longitud en bits de 3 = longitud en bits del símbolo anterior ‘b’, que es 3. Incremente el código del símbolo anterior en 1 y asígnelo a ‘d’. 
Código para ‘d’: 111

Paso 3: Resultado final.  

Personaje Códigos canónicos de Huffman
C 0
a 10
b 110
d 111

La ventaja básica de este método es que la información de codificación que se pasa al decodificador se puede hacer más compacta y eficiente en memoria. Por ejemplo, uno puede simplemente pasar las longitudes de bits de los caracteres o símbolos al decodificador. Los códigos canónicos se pueden generar fácilmente a partir de las longitudes, ya que son secuenciales. 
Para generar códigos Huffman utilizando Huffman Tree, consulte aquí .

Enfoque: un enfoque simple y eficiente es generar un árbol de Huffman para los datos y usar una estructura de datos similar a TreeMap en Java para almacenar los símbolos y las longitudes de bits de modo que la información siempre permanezca ordenada. Luego, los códigos canónicos se pueden obtener mediante operaciones de incremento y desplazamiento a la izquierda bit a bit. 

Java

// Java Program for Canonical Huffman Encoding
 
import java.io.*;
import java.util.*;
 
// Nodes of Huffman tree
class Node {
 
    int data;
    char c;
 
    Node left;
    Node right;
}
 
// comparator class helps to compare the node
// on the basis of one of its attribute.
// Here we will be compared
// on the basis of data values of the nodes.
class Pq_compare implements Comparator<Node> {
    public int compare(Node a, Node b)
    {
 
        return a.data - b.data;
    }
}
 
class Canonical_Huffman {
 
    // Treemap to store the
    // code lengths(sorted) as keys
    // and corresponding(sorted)
    // set of characters as values
    static TreeMap<Integer, TreeSet<Character> > data;
 
    // Constructor to initialize the Treemap
    public Canonical_Huffman()
    {
        data = new TreeMap<Integer, TreeSet<Character> >();
    }
 
    // Recursive function
    // to generate code lengths
    // from regular Huffman codes
    static void code_gen(Node root, int code_length)
    {
        if (root == null)
            return;
 
        // base case; if the left and right are null
        // then its a leaf node.
        if (root.left == null && root.right == null) {
 
            // check if key is present or not.
            // If not present add a new treeset
            // as value along with the key
            data.putIfAbsent(code_length, new TreeSet<Character>());
 
            // c is the character in the node
            data.get(code_length).add(root.c);
            return;
        }
 
        // Add 1 when on going left or right.
        code_gen(root.left, code_length + 1);
        code_gen(root.right, code_length + 1);
    }
 
    static void testCanonicalHC(int n, char chararr[], int freq[])
    {
 
        // min-priority queue(min-heap).
        PriorityQueue<Node> q
            = new PriorityQueue<Node>(n, new Pq_compare());
 
        for (int i = 0; i < n; i++) {
 
            // creating a node object
            // and adding it to the priority-queue.
            Node node = new Node();
 
            node.c = chararr[i];
            node.data = freq[i];
 
            node.left = null;
            node.right = null;
 
            // add functions adds
            // the node to the queue.
            q.add(node);
        }
 
        // create a root node
        Node root = null;
 
        // extract the two minimum value
        // from the heap each time until
        // its size reduces to 1, extract until
        // all the nodes are extracted.
        while (q.size() > 1) {
 
            // first min extract.
            Node x = q.peek();
            q.poll();
 
            // second min extract.
            Node y = q.peek();
            q.poll();
 
            // new node f which is equal
            Node nodeobj = new Node();
 
            // to the sum of the frequency of the two nodes
            // assigning values to the f node.
            nodeobj.data = x.data + y.data;
            nodeobj.c = '-';
 
            // first extracted node as left child.
            nodeobj.left = x;
 
            // second extracted node as the right child.
            nodeobj.right = y;
 
            // marking the f node as the root node.
            root = nodeobj;
 
            // add this node to the priority-queue.
            q.add(nodeobj);
        }
 
        // Creating a canonical Huffman object
        Canonical_Huffman obj = new Canonical_Huffman();
 
        // generate code lengths by traversing the tree
        code_gen(root, 0);
 
        // Object array to store the keys
        Object[] arr = data.keySet().toArray();
 
        // Set initial canonical code=0
        int c_code = 0, curr_len = 0, next_len = 0;
 
        for (int i = 0; i < arr.length; i++) {
            Iterator it = data.get(arr[i]).iterator();
 
            // code length of current character
            curr_len = (int)arr[i];
 
            while (it.hasNext()) {
 
                // Display the canonical codes
                System.out.println(it.next() + ":"
                                   + Integer.toBinaryString(c_code));
 
                // if values set is not
                // completed or if it is
                // the last set set code length
                // of next character as current
                // code length
                if (it.hasNext() || i == arr.length - 1)
                    next_len = curr_len;
                else
                    next_len = (int)arr[i + 1];
 
                // Generate canonical code
                // for next character using
                // regular code length of next
                // character
                c_code = (c_code + 1) << (next_len - curr_len);
            }
        }
    }
 
    // Driver code
    public static void main(String args[]) throws IOException
    {
        int n = 4;
        char[] chararr = { 'a', 'b', 'c', 'd' };
        int[] freq = { 10, 1, 15, 7 };
        testCanonicalHC(n, chararr, freq);
    }
}
Producción: 

c:0
a:10
b:110
d:111

 

Publicación traducida automáticamente

Artículo escrito por SouravAChowdhury_97 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *