logo

Huffman kódování Java

Huffmanův kódovací algoritmus navrhl David A. Huffman v roce 1950. Jedná se o a bezztrátovou kompresi dat mechanismus. Je také známý jako kódování komprese dat. Je široce používán v kompresi obrázků (JPEG nebo JPG). V této části budeme diskutovat o Huffmanovo kódování a dekódování, a také implementovat svůj algoritmus v programu Java.

Víme, že každý znak je posloupností 0 a 1 a ukládá se pomocí 8 bitů. Mechanismus se nazývá kódování s pevnou délkou protože každý znak používá stejný počet pevného bitového úložiště.

homogenní směs

Tady vyvstává otázka, je možné snížit množství místa potřebného k uložení postavy?

Ano, je to možné pomocí kódování s proměnnou délkou. V tomto mechanismu využíváme některé znaky, které se vyskytují častěji než jiné znaky. V této technice kódování můžeme reprezentovat stejný kus textu nebo řetězec snížením počtu bitů.

Huffmanovo kódování

Huffmanovo kódování implementuje následující kroky.

  • Všem daným znakům přiřadí kód proměnné délky.
  • Délka kódu znaku závisí na tom, jak často se vyskytuje v daném textu nebo řetězci.
  • Znak dostane nejmenší kód, pokud se vyskytuje často.
  • Znak získá největší kód, pokud se vyskytuje nejméně.

Huffmanovo kódování následuje a pravidlo předpony což zabraňuje nejednoznačnosti při dekódování. Pravidlo také zajišťuje, že kód přiřazený znaku není považován za předponu kódu přiřazeného jinému znaku.

Huffmanovo kódování zahrnuje následující dva hlavní kroky:

  • Nejprve sestrojte a Huffmanův strom z daného vstupního řetězce nebo znaků nebo textu.
  • Přiřaďte Huffmanův kód každé postavě přecházením přes strom.

Pojďme si stručně představit dva výše uvedené kroky.

Huffmanův strom

Krok 1: Pro každý znak uzlu vytvořte listový uzel. Listový uzel znaku obsahuje frekvenci tohoto znaku.

Krok 2: Nastavte všechny uzly v seřazeném pořadí podle jejich frekvence.

Krok 3: Může existovat stav, kdy dva uzly mohou mít stejnou frekvenci. V takovém případě proveďte následující:

  1. Vytvořte nový vnitřní uzel.
  2. Frekvence uzlu bude součtem frekvence těchto dvou uzlů, které mají stejnou frekvenci.
  3. Označte první uzel jako levý potomek a další uzel jako pravý potomek nově vytvořeného interního uzlu.

Krok 4: Opakujte kroky 2 a 3, dokud všechny uzly nevytvoří jeden strom. Tak získáme Huffmanův strom.

Příklad Huffmanova kódování

Předpokládejme, že musíme zakódovat řetězec Abra Cadabra. Určete následující:

  1. Huffmanův kód pro všechny postavy
  2. Průměrná délka kódu pro daný řetězec
  3. Délka zakódovaného řetězce

(i) Huffmanův kód pro všechny postavy

Abychom určili kód pro každý znak, nejprve sestrojíme a Huffmanův strom.

Krok 1: Vytvořte dvojice znaků a jejich frekvence.

(a, 5), (b, 2), (c, 1), (d, 1), (r, 2)

Krok 2: Seřaďte dvojice podle frekvence, dostaneme:

trimovací řetězec java

(c, 1), (d, 1), (b, 2) (r, 2), (a, 5)

Krok 3: Vyberte první dva znaky a připojte je pod nadřazený uzel.

Huffman kódování Java

Pozorujeme, že nadřazený uzel nemá frekvenci, takže mu musíme frekvenci přiřadit. Frekvence nadřazeného uzlu bude součtem jeho podřízených uzlů (levý a pravý), tj. 1+1= 2.

Huffman kódování Java

Krok 4: Opakujte kroky 2 a 3, dokud nezískáte jediný strom.

Pozorujeme, že dvojice jsou již seřazeny (podle kroku 2) způsobem. Opět vyberte první dva páry a připojte je.

Huffman kódování Java

Pozorujeme, že nadřazený uzel nemá frekvenci, takže mu musíme frekvenci přiřadit. Frekvence nadřazeného uzlu bude součtem jeho podřízených uzlů (levý a pravý), tj. 2+2= 4.

c řetězec v poli
Huffman kódování Java

Opět zkontrolujeme, zda jsou dvojice seřazené nebo ne. V tomto kroku musíme páry seřadit.

Huffman kódování Java

Podle kroku 3 vyberte první dva páry a spojte je, získáme:

Huffman kódování Java

Pozorujeme, že nadřazený uzel nemá frekvenci, takže mu musíme frekvenci přiřadit. Frekvence nadřazeného uzlu bude součtem jeho podřízených uzlů (levý a pravý), tj. 2+4= 6.

Huffman kódování Java

Opět zkontrolujeme, zda jsou dvojice seřazené nebo ne. V tomto kroku musíme páry seřadit. Po seřazení strom vypadá takto:

Huffman kódování Java

Podle kroku 3 vyberte první dva páry a spojte je, získáme:

Huffman kódování Java

Pozorujeme, že nadřazený uzel nemá frekvenci, takže mu musíme frekvenci přiřadit. Frekvence nadřazeného uzlu bude součtem jeho podřízených uzlů (levý a pravý), tj. 5+6= jedenáct.

řetězec nahradit vše java
Huffman kódování Java

Získáme tedy jediný strom.

Nakonec najdeme kód pro každý znak pomocí výše uvedeného stromu. Každé hraně přiřaďte váhu. Všimněte si, že každý vážený levý okraj je 0 a vážený pravý okraj je 1.

Huffman kódování Java

Pozorujeme, že vstupní znaky jsou prezentovány pouze v opouštěcích uzlech a vnitřní uzly mají hodnoty null. Chcete-li najít Huffmanův kód pro každý znak, přejděte přes Huffmanův strom od kořenového uzlu k listovému uzlu konkrétního znaku, pro který chceme najít kód. Tabulka popisuje kód a délku kódu pro každý znak.

Charakter Frekvence Kód Délka kódu
A 5 0 1
B 2 111 3
C 1 1100 4
D 1 1101 4
R 2 10 2

Pozorujeme, že nejčastější znak má nejkratší délku kódu a méně častý znak má největší délku kódu.

Nyní můžeme řetězec zakódovat (Abra Cadabra) které jsme vzali výše.

 0 111 10 0 1100 0 1101 0 111 10 0 

(ii) Průměrná délka kódu pro řetězec

Průměrnou délku kódu Huffmanova stromu lze určit pomocí vzorce uvedeného níže:

 Average Code Length = ∑ ( frequency × code length ) / ∑ ( frequency ) 

= { (5 x 1) + (2 x 3) + (1 x 4) + (1 x 4) + (2 x 2) } / (5+2+1+1+2)

= 2,09090909

(iii) Délka kódovaného řetězce

Délku zakódované zprávy lze určit pomocí následujícího vzorce:

 length= Total number of characters in the text x Average code length per character 

= 11 x 2,09090909

= 23 bitů

Huffmanův kódovací algoritmus

 Huffman (C) n=|C| Q=C for i=1 to n-1 do z=allocate_Node() x=left[z]=Extract_Min(Q) y=right[z]=Extract_Min(Q) f[z]=f[x]+f[y] Insert(Q,z) return Extract_Min(Q) 

Huffmanův algoritmus je chamtivý algoritmus. Protože v každé fázi algoritmus hledá nejlepší dostupné možnosti.

Časová složitost Huffmanova kódování je O(nlogn). Kde n je počet znaků v daném textu.

Huffmanovo dekódování

Huffmanovo dekódování je technika, která převádí zakódovaná data na počáteční data. Jak jsme viděli u kódování, Huffmanův strom je vytvořen pro vstupní řetězec a znaky jsou dekódovány na základě jejich pozice ve stromu. Proces dekódování je následující:

programovací vzory java
  • Začněte přecházet přes strom z vykořenit uzel a vyhledejte postavu.
  • Pokud se posuneme v binárním stromu doleva, přidejte 0 ke kódu.
  • Pokud se pohybujeme přímo v binárním stromu, přidejte 1 ke kódu.

Podřízený uzel obsahuje vstupní znak. Je mu přiřazen kód tvořený následujícími 0s a 1s. Časová složitost dekódování řetězce je Na), kde n je délka řetězce.

Program Java pro kódování a dekódování Huffman

V následujícím programu jsme použili datové struktury, jako jsou prioritní fronty, zásobníky a stromy, k návrhu logiky komprese a dekomprese. Naše nástroje budeme zakládat na široce používané algoritmické technice Huffmanova kódování.

HuffmanCode.java

 import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue; //defining a class that creates nodes of the tree class Node { //storing character in ch variable of type character Character ch; //storing frequency in freq variable of type int Integer freq; //initially both child (left and right) are null Node left = null; Node right = null; //creating a constructor of the Node class Node(Character ch, Integer freq) { this.ch = ch; this.freq = freq; } //creating a constructor of the Node class public Node(Character ch, Integer freq, Node left, Node right) { this.ch = ch; this.freq = freq; this.left = left; this.right = right; } } //main class public class HuffmanCode { //function to build Huffman tree public static void createHuffmanTree(String text) { //base case: if user does not provides string if (text == null || text.length() == 0) { return; } //count the frequency of appearance of each character and store it in a map //creating an instance of the Map Map freq = new HashMap(); //loop iterates over the string and converts the text into character array for (char c: text.toCharArray()) { //storing character and their frequency into Map by invoking the put() method freq.put(c, freq.getOrDefault(c, 0) + 1); } //create a priority queue that stores current nodes of the Huffman tree. //here a point to note that the highest priority means the lowest frequency PriorityQueue pq = new PriorityQueue(Comparator.comparingInt(l -&gt; l.freq)); //loop iterate over the Map and returns a Set view of the mappings contained in this Map for (var entry: freq.entrySet()) { //creates a leaf node and add it to the queue pq.add(new Node(entry.getKey(), entry.getValue())); } //while loop runs until there is more than one node in the queue while (pq.size() != 1) { //removing the nodes having the highest priority (the lowest frequency) from the queue Node left = pq.poll(); Node right = pq.poll(); //create a new internal node with these two nodes as children and with a frequency equal to the sum of both nodes&apos; frequencies. Add the new node to the priority queue. //sum up the frequency of the nodes (left and right) that we have deleted int sum = left.freq + right.freq; //adding a new internal node (deleted nodes i.e. right and left) to the queue with a frequency that is equal to the sum of both nodes pq.add(new Node(null, sum, left, right)); } //root stores pointer to the root of Huffman Tree Node root = pq.peek(); //trace over the Huffman tree and store the Huffman codes in a map Map huffmanCode = new HashMap(); encodeData(root, &apos;&apos;, huffmanCode); //print the Huffman codes for the characters System.out.println(&apos;Huffman Codes of the characters are: &apos; + huffmanCode); //prints the initial data System.out.println(&apos;The initial string is: &apos; + text); //creating an instance of the StringBuilder class StringBuilder sb = new StringBuilder(); //loop iterate over the character array for (char c: text.toCharArray()) { //prints encoded string by getting characters sb.append(huffmanCode.get(c)); } System.out.println(&apos;The encoded string is: &apos; + sb); System.out.print(&apos;The decoded string is: &apos;); if (isLeaf(root)) { //special case: For input like a, aa, aaa, etc. while (root.freq-- &gt; 0) { System.out.print(root.ch); } } else { //traverse over the Huffman tree again and this time, decode the encoded string int index = -1; while (index <sb.length() - 1) { index="decodeData(root," index, sb); } traverse the huffman tree and store codes in a map function that encodes data public static void encodedata(node root, string str, huffmancode) if (root="=" null) return; checks node is leaf or not (isleaf(root)) huffmancode.put(root.ch, str.length()> 0 ? str : &apos;1&apos;); } encodeData(root.left, str + &apos;0&apos;, huffmanCode); encodeData(root.right, str + &apos;1&apos;, huffmanCode); } //traverse the Huffman Tree and decode the encoded string function that decodes the encoded data public static int decodeData(Node root, int index, StringBuilder sb) { //checks if the root node is null or not if (root == null) { return index; } //checks if the node is a leaf node or not if (isLeaf(root)) { System.out.print(root.ch); return index; } index++; root = (sb.charAt(index) == &apos;0&apos;) ? root.left : root.right; index = decodeData(root, index, sb); return index; } //function to check if the Huffman Tree contains a single node public static boolean isLeaf(Node root) { //returns true if both conditions return ture return root.left == null &amp;&amp; root.right == null; } //driver code public static void main(String args[]) { String text = &apos;javatpoint&apos;; //function calling createHuffmanTree(text); } } </sb.length()>

Výstup:

 Huffman Codes of the characters are: {p=000, a=110, t=111, v=001, i=010, j=011, n=100, o=101} The initial string is: javatpoint The encoded string is: 011110001110111000101010100111 The decoded string is: javatpoint