2013. 4. 9. 15:47 카테고리 없음

허프만 부호화(Huffman coding)

허프만 부호화(Huffman coding)


허프만 부호화는 문자들의 빈도로부터 접두 부호(어떤 한 문자에 대한 부호가 다른 부호들의 접두어가 되지 않는 부호)를 만들어 내는 알고리즘으로, 적게 나오는 문자일수록 더 긴 부호를 쓰고 많이 나올수록 더 짧은 부호를 쓴다. 허프만 부호화는 주어진 빈도에 대해서 항상 최적의 접두 부호를 만들어 내며, 이 과정은 빈도가 정렬되어 있을 경우 O(n)만에 가능하다. 각 문자들의 빈도가 2의 거듭제곱 꼴이거나 모두 같을 경우 이 접두 부호는 간단한 이진 블록 부호와 동일하다.


1. 초기화 : 모든 기호를 출현 빈도수에 따라 나열한다.

2. 단 한 가지 기호가 남을 때까지 아래 단계를 반복한다.

2.1 목록으로부터 가장 빈도가 낮은 것을 2개 고른다.

2.2 그 다음 허프만이 두가지 기호를 부모 노드를 가지는 부트리를 구성하고 자식노드를 생성한다. 부모 노드 단 기호들의 빈도수를

더하여 주 노드에 할당하고 목록의 순서에 맞도록 목록에 삽입한다.

2.3 목록에서 부모노드에 포함된 기호를 제거한다.


허프만 알고리즘은 입력 기호를 잎으로 하는 이진 트리를 만들어서 접두 부호를 만들어 내는 알고리즘이다.


출처: http://ko.wikipedia.org/wiki/허프만_부호화

01 // Huffman_test.java
02  
03 import java.io.*;
04  
05 public class Huffman_test{
06     public static void main(String[] args){
07          
08         String str;
09  
10         try{
11             /* 읽을 파일을 지정해서 열기 */
12             BufferedReader fin = new BufferedReader(new FileReader("test.txt"));
13              
14             System.out.println("____________________________");
15             System.out.println("HUFFMAN CODING");
16             System.out.println("CODE BY DAEGYEONG KIM");
17             System.out.println("FROM DANKOOK UNIVERSITY");
18             System.out.println("____________________________\n");
19  
20  
21             System.out.println("[+] Reading from the file...");
22             str = fin.readLine();
23             System.out.println("[+] The object of Huffman Tree is being made...");
24             Huffman_tree Huff_tree = new Huffman_tree();
25             System.out.println("[+] Alphabets are being counted...");
26             Huff_tree.count_alphabet(str);
27             System.out.println("[+] Each alphabet is being put into heap...");
28             Huff_tree.heapify();
29             System.out.println("\n[+] The counted numbers of alphabets are being treeized..");
30             Huff_tree.treeize();
31             System.out.println("\n[+] The counted numbers of alphabets are being tablized...");
32             Huff_tree.tablize();
33             System.out.println("\n[+] Each alphabet's Huffman code is being generated...");
34             Huff_tree.printTable();
35             System.out.println("\n[+] The input data is being Huffman codized...");
36             System.out.println(Huff_tree.huffmanCodize());
37             System.out.println("\n[+] The program normally exited!");
38  
39             fin.close();        /* 파일 닫기 */
40         }catch(Exception e){
41             System.out.println("\n[-] Fail: " + e);
42         }
43     }
44 }

 
001 /**
002  *  input a alphabet-written file and find out the frequency of each alphabet
003  *  make a Huffman tree(decode free), covert the string in the file to Huffman code
004  
005  *  ASCII codes which can be used for the Huffman coding:
006  *  'space'!"#$%^'()*+,-./0123456789:;<">?ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
007  *  abcdefghijklmnopqrstuvwxyz
008  
009  *  Coded by Daegyeong Kim
010  *  Dankook Univ.
011  *  April 28, 2012
012  
013  **/
014  
015 // Huffman_tree.java
016  
017 public class Huffman_tree{
018      
019     final int NUM_OF_ASCII = 122;
020     private TreeNode rootNode;      /* 루트 노드 */
021     private MinHeap minHeap;        /* 최소히프 */
022     private String input;           /* 입력 문자열 */
023     Huffman_hashtable [] huffTable = new Huffman_hashtable[NUM_OF_ASCII];   /* 해쉬테이블 */
024  
025     public Huffman_tree(){     
026         minHeap = new MinHeap(NUM_OF_ASCII);            /* 최소히프 객체 생성 */
027          
028         for (int i = 32; i < NUM_OF_ASCII; i++){
029             huffTable[i] = new Huffman_hashtable();     /* one alphabet, one object
030                                                            index = Ascii code
031                                                            1:1 대응을 통하여 해쉬테이블화 */
032         }
033     }
034      
035     public void count_alphabet(String theInput){
036         input = theInput;
037         /* upper case: 65-90, lower case: 97-122, Space: 32 */
038         for (int i = 0; i < input.length(); i++){        /* 입력받은 문자열을 전체에 대하여 */
039             int ascii = (int)input.charAt(i);           /* i에 해당하는 아스키값으로 변환 */
040             huffTable[ascii].count++;                   /* 알파벳 하나하나 count */
041         }
042     }
043      
044     public void heapify(){
045         for (int i = 32; i < NUM_OF_ASCII; i++){
046             if (huffTable[i].count != 0 ){              /* 1개 이상 존재하는 알파벳에 대하여 */
047                 TreeNode newNode = new TreeNode();      /* 새 노드 객체를 만들어서 */
048                 newNode.setElement(huffTable[i].count); /* 객체에 카운트 된 숫자를 넣고 */
049                 newNode.setAscii((char)i);              /* Ascii값을 넣고 */
050                 minHeap.put(newNode);                   /* 객체를 히프에 넣음 */
051             }
052         }
053         System.out.println(minHeap.toString());
054     }
055      
056     private TreeNode choose(){
057         return minHeap.removeMin();                 /* return the minimum element's index */
058     }
059      
060     /* Making a tree */
061     public void treeize(){
062         for (;minHeap.size() > 1;) {                 /* 노드 하나가 남을 때까지 반복 */
063             TreeNode newNode = new TreeNode();
064             newNode.setLeftLink(choose());          /* 제일 작은 수를 뽑아서 왼쪽 자식 */
065             newNode.setRightLink(choose());         /* 그 다음 제일 작은 수를 뽑아서 오른쪽 자식 */
066             newNode.setElement(newNode.getLeftLink().getElement() /* 왼쪽 자식과 */
067                     + newNode.getRightLink().getElement()); /* 오른쪽 자식의 길이를 합산하여 저장 후 */
068             minHeap.put(newNode);                   /* 히프에 넣음 */
069         }
070         rootNode = minHeap.removeMin();             /* 루트 노드를 반환 */
071     }
072      
073     /* Fulfil the table */
074     public void tablize(){
075         for (int i = 32; i < NUM_OF_ASCII; i++){
076             huffTable[i].setAscii(i);               /* 해쉬테이블에 Ascii 값을 채워넣음 */
077         }
078         System.out.print("Preordered Tree is [");
079         preOrderTree(rootNode);                     /* 전위순회하면서 Huffman code를 구함 */
080         System.out.print("]\n");
081     }
082      
083     /* preorder traversal for assign Huffman code */
084     private void preOrderTree(TreeNode currNode){
085         if (currNode == null){
086             return;
087         }
088         if (currNode.getLeftLink() != null){        /* 왼쪽자식이 있으면 왼쪽자식에 "0"을 더함 */
089             currNode.getLeftLink().setHuffmanCode(currNode.getHuffmanCode() + "0");
090         }
091         if (currNode.getRightLink() != null){       /* 오른쪽자식이 있으면 오른쪽자식에 "1"을 더함 */
092             currNode.getRightLink().setHuffmanCode(currNode.getHuffmanCode() + "1");
093         }
094  
095 //      System.out.print(currNode.getAscii());
096         System.out.print(currNode.getElement() + ", ");
097 //      System.out.print(currNode.getHuffmanCode() + " ");
098          
099         if (currNode.getAscii() != 0){          /* 단말노드이면(단말노드에는 ASCII값이 저장되어 있음) */
100             /* 단말노드의 Huffman Code를 해쉬테이블에 저장함 */
101             huffTable[(int)currNode.getAscii()].huffCode = currNode.getHuffmanCode();
102         }
103          
104         preOrderTree(currNode.getLeftLink());   /* 왼쪽 탐색 */
105         preOrderTree(currNode.getRightLink());  /* 오른쪽 탐색 */
106     }
107      
108     /* 해쉬 테이블 출력 */
109     public void printTable(){
110         System.out.println("Ascii  Char   count   Huffman_code");
111         for (int i = 32; i < NUM_OF_ASCII; i++){
112             System.out.println("  " + huffTable[i].ascii + "     "
113                     + huffTable[i].letter + "      "
114                     + huffTable[i].count + "      "
115                     + huffTable[i].huffCode);
116         }
117     }
118      
119     /* 각 알파벳을 Huffman code로 변환 */
120     public StringBuffer huffmanCodize(){
121         StringBuffer sb = new StringBuffer();
122         for (int i = 0; i < input.length(); i++){
123             sb.append(huffTable[(int)input.charAt(i)].huffCode);
124         }
125         return sb;
126     }
127 }


 
01 // Huffman_hashtable.java
02  
03 public class Huffman_hashtable{
04     public char letter;             /* character for the Ascii code */
05     public int ascii;               /* Ascii code */
06     public int count;               /* frequency */
07     public StringBuffer huffCode;   /* huffman code */
08      
09     public Huffman_hashtable(){
10         count = 0;
11         huffCode = new StringBuffer();
12     }
13      
14     public void setAscii(int theAscii){
15         ascii = theAscii;
16         letter = (char)theAscii;
17     }
18 }


 
001 // MinHeap.java
002  
003 /* import utilities.ChangeArrayLength ; */
004  
005 public class MinHeap{
006     /* data members */
007     TreeNode [] heap;   /* array for complete binary tree */
008     int size;           /* number of elements in heap */
009  
010     /** create a heap with the given initial capacity
011      * @throws IllegalArgumentException when
012      * initialCapacity < 1 */
013     /* constructors : 초기 크기를 매개변수로 넘겨줌 */
014     public MinHeap(int initialCapacity){
015         if (initialCapacity < 1){
016             throw new IllegalArgumentException
017             ("initialCapacity must be >= 1");
018         }
019         /* 인덱스 0를 사용하지 않기 위해서 "+1" */
020         heap = new TreeNode [initialCapacity + 1];
021         size = 0;
022     }
023  
024     /** create a heap with initial capacity 10 */
025     public MinHeap()
026     {this(10);}
027  
028     /* methods */
029     /** @return true iff the heap is empty */
030     public boolean isEmpty()
031     {return size == 0;}
032  
033     /** @return number of elements in the heap */
034     public int size()
035     {return size;}
036  
037     /** @return maximum element
038      * @return null if the heap is empty */
039     /* 빈 배열일 경우 null, if not, 인덱스 1의 원소를 반환 */
040     public TreeNode getMin(){
041         return (size == 0) ? null : heap[1];
042     }
043  
044     /** put theElement into the heap */
045     public void put(TreeNode treeNode){
046         /* increase array size if necessary
047               오류 처리 후 종료 */
048         if (size == heap.length - 1)
049             System.out.println("Heap is full. No insertion.");
050         /*  동적으로 2배 크기만큼 할당하여 기존 것을 copy하여 삽입 수행
051             heap = (TreeNode []) ChangeArrayLength.changeLength1D
052                                                 (heap, 2 * heap.length); */
053         else{
054             /* find place for theElement
055                currentNode starts at new leaf and moves up tree */
056             int currentNode = ++size;
057             while (currentNode != 1 &&
058                     /* 부모와 삽입할 노드를 비교하여  */
059                     heap[currentNode / 2].getElement() > treeNode.getElement())
060             {
061                 /* cannot put theElement in heap[currentNode] */
062                 heap[currentNode] = heap[currentNode / 2]; /* move element down */
063                 /* 부모를 자식노드로 옮김 */
064                 currentNode /= 2;                          /* move to parent */
065             }
066  
067             /* 부모노드보다 크다면 그 자리에 삽입 */
068             heap[currentNode] = treeNode;
069         }
070     }
071  
072     /** remove max element and return it */
073     public TreeNode removeMin(){
074         /* if heap is empty return null */
075         if (size == 0) return null;       /* heap empty */
076  
077         /* 나중에 리턴할 값 */
078         TreeNode minElement = heap[1];  /* max element */
079  
080         /* 마지막에 있는 노드를 삭제하고 루트에 임시로 넣음 */
081         /* reheapify */
082         TreeNode lastElement = heap[size--];
083  
084         /* find place for lastElement starting at root */
085         int currentNode = 1,
086         child = 2;     /* child of currentNode */
087         while (child <= size){
088             /* heap[child] should be smaller child of currentNode */
089             if (child < size && (heap[child].getElement() > heap[child + 1].getElement()))
090                 child++;
091  
092             /* can we put lastElement in heap[currentNode]? */
093             if (lastElement.getElement() <= heap[child].getElement())
094                 break;   /* yes */
095  
096             /* no */
097             heap[currentNode] = heap[child]; /* move child up */
098             currentNode = child;             /* move down a level */
099             child *= 2;
100         }
101         heap[currentNode] = lastElement;
102  
103         return minElement;
104     }
105  
106     /* 힙의 내용을 모두 출력 */
107     public String toString(){
108         StringBuffer s = new StringBuffer();
109         s.append("The " + size + "-element minHeap has [");
110         if (size > 0)
111         {/* nonempty heap */
112             /* do first element */
113             s.append(heap[1].getElement());
114             /* do remaining elements */
115             for (int i = 2; i <= size; i++){
116                 s.append(", " + heap[i].getElement());
117             }
118         }
119         s.append("]");
120  
121         return new String(s);
122     }
123 }


 
01 // TreeNode.java
02  
03 public class TreeNode
04 {
05     private int element;
06     private TreeNode leftLink;          /* left subtree */
07     private TreeNode rightLink;         /* right subtree */
08     private char ascii;                 /* leaf node has its own Ascii code */
09     private StringBuffer huffmanCode;   /* Huffman code */
10      
11     /* constructors */
12     public TreeNode(){
13         huffmanCode = new StringBuffer();
14     }
15  
16     public TreeNode(int theElement){
17         element = theElement;
18         huffmanCode = new StringBuffer();
19     }
20  
21     public TreeNode(int theElement,
22                     TreeNode theleftLink,
23                     TreeNode therightLink)
24     {
25         element = theElement;
26         leftLink = theleftLink;
27         rightLink = therightLink;
28         huffmanCode = new StringBuffer();
29     }
30  
31     /* accessor methods */
32     public TreeNode getLeftLink(){
33         return leftLink;
34     }
35     public TreeNode getRightLink(){
36         return rightLink;
37     }
38     public int getElement(){
39         return element;
40     }
41     public char getAscii(){
42         return ascii;
43     }
44     public StringBuffer getHuffmanCode(){
45         return huffmanCode;
46     }
47  
48     /* mutator methods */
49     public void setLeftLink(TreeNode theLeftLink){
50         leftLink = theLeftLink;
51     }
52     public void setRightLink(TreeNode theRightLink){
53         rightLink = theRightLink;
54     }
55     public void setElement(int theElement){
56         element = theElement;
57     }
58     public void setAscii(char theAscii){
59         ascii = theAscii;
60     }
61     public void setHuffmanCode(String code){
62         huffmanCode.append(code);
63     }
64 }

 

출처 : http://tyche87.tistory.com/208 

posted by townone