mojo's Blog

Huffman Coding 본문

학교에서 들은거 정리/알고리즘설계와분석

Huffman Coding

_mojo_ 2021. 11. 16. 18:20

0-1 Knapsack Example 2: n = 6, W = 10

 

 

알고리즘 별로 획득되는 이익이 다른것을 알 수 있다.

위 예제에서 그나마 좋은 알고리즘이 greedy 알고리즘인데 이 중에 최대 이익을 뽑아낼 수 있는 알고리즘을

활용하여 O(nlogn) 의 효율을 뽑아서 이익을 뽑도록 한다.

 

 

The Greedy Methods

 

A technique to follow the problem-solving heuristic of making the locally optimal choice at each stage.

 

※ Strategy

- 매 순간마다 가장 최선인 것을 선택하는 방법이다.

- 즉, locally optimal를 선택함으로써 globally optimal을 도달하려는 것이 궁극적인 목표이다.

 

※ Pros and cons

- 알고리즘을 simple, straightforward 하게 설계한다.

- 그러나 모든 문제에 대한 optimal solution을 보장하지 못한다는 점이 있다.

 

 

Huffman Coding

 

※ Data compression

- Data compression can save storage space for files.

- Huffman coding is just one of many data compression techniques.

 

※ Problem

- 파일이 주어질 때, file 안에 담긴 character들에 대한 binary character code를 발견하는 것이다.

- 그것도 가장 적은 bit 들로 표현이 가능해야 한다.

 

예시를 살펴보도록 하자.

원래의 텍스트 파일 "ababcbbbc" 가 있다고 해보자.

Huffman code 로 a = 10, b = 0, c = 11 이 주어졌다고 할 때, 다음과 같이 encoding 가능하다.

Compressed file : 1001001100011

 

 

Prefix Codes

 

Huffman code 로 encoding을 하고 그 다음에 유일하게 decoding 이 가능해야 의미가 있다.

다음 예제를 살펴보도록 한다. 

 

Example 1 : a = 00, b = 1110, c = 110, ...

111000110... 가 존재한다고 할 때, 보통 character를 왼쪽에서부터 접근하는 방법을 이용한다.

즉, 111000110... 과 같이 decoding 하면 bac... 와 같이 된다. 

 

Example 2 : a = 00, b = 1100, c = 110, ...

1100000... 가 존재한다고 할 때, conflict가 발생하는 것으로 보인다.

맨 처음에 11 다음에 1100 으로 잡아서 b라고 할지, 110 으로 잡아서 c라고 할지에 대한 conflict가 발생한다.

 

※  Binary trees corresponding to prefix codes

- The code of a character c is the label of the path from the root to c.

- Decoding of an encoded file is trivial.

 

 

 

※ Problem

- character set V = {v1, v2, ..., vn} 으로 인코딩된 주어진 file 에 대해서 다음과 같은 cost function을 최소화하는

    binary tree T으로 구성된 optimal prefix binary code를 찾아야 한다.

cost function은 다음과 같다.

 

bits(T) = freq(v1) * depth(v1) + ... + freq(vn) * depth(vn)

 

freq(vi) : 해당 문자 vi가 file에서 나타난 빈도수를 의미한다.

depth(vi) : 해당 문자 vi의 tree T의 깊이를 의미한다.

 

A Greedy approach successfully finds an optimal code.

 

 

Huffman's Algorithm

 

※ A greedy approach

- Repeat the following until only one tree is left:

  ① Start from a set of single node trees

  ② Pick up two trees u and v with the lowest frequencies (min heap 이용)

  ③ Merge them by adding a root node w wheree the frequency of the new node is 

         the sum of those of u and v.

  ④ Replace u and v by w.

 

 

 

Solution Code

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

typedef struct _node {
	char symbol;
	int freq;
	struct _node* left;
	struct _node* right;
} NODE;

NODE* u, * v, * w;
NODE* node[7];

char symbolSet[7] = { '_','b','e','c','a','d','f' };
int freqSet[7] = { -1,5,10,12,16,17,25 };
int n = 6, node_cnt, total_freq;

void PQ_insert(NODE* tmp) 
{
	int x;

	x = ++node_cnt;
	while ((x != 1) && (tmp->freq < node[x / 2]->freq)) {
		node[x] = node[x / 2];
		x /= 2;
	}
	node[x] = tmp;
}

NODE* PQ_delete()
{
	int parent, child;
	NODE *item, *temp;

	item = node[1], temp = node[node_cnt--];
	parent = 1, child = 2;
	while (child <= node_cnt) {
		if ((child < node_cnt) && (node[child]->freq > node[child + 1]->freq)) child++;
		if (temp->freq <= node[child]->freq) break;
		node[parent] = node[child];
		parent = child;
		child *= 2;
	}
	node[parent] = temp;
	return item;
}

NODE* make_a_new_node() 
{
	return (NODE*)malloc(sizeof(NODE));
}

void Set_Single_Node(char s, int f) 
{
	int x;
	NODE* tmp;
	
	tmp = make_a_new_node();
	tmp->symbol = s;
	tmp->freq = f;
	tmp->left = tmp->right = nullptr;
	PQ_insert(tmp);
}

void Print_Tree(NODE* tmp, int depth, char *encoding) {
	if (tmp) {
		encoding[depth] = '0';
		Print_Tree(tmp->left, depth + 1, encoding);
		if (tmp->left == nullptr && tmp->right == nullptr) {
			encoding[depth] = '\0';
			printf(" [%c] | %6s | %5.2f %%\n", tmp->symbol, encoding, 
				((float)tmp->freq / (float)total_freq) * (float)100);
		}
		encoding[depth] = '1';
		Print_Tree(tmp->right, depth + 1, encoding);
	}
}

void Find_Depth(NODE* tmp, int depth, int& max_depth) 
{
	if (tmp) {
		Find_Depth(tmp->left, depth + 1, max_depth);
		if (tmp->left == nullptr && tmp->right == nullptr)
			max_depth = max_depth < depth ? depth : max_depth;
		Find_Depth(tmp->right, depth + 1, max_depth);
	}
}

void Huffman_Coding()
{
	int i, tree_depth;
	char* encoding;

	for (i = 1; i <= n; i++) {
		Set_Single_Node(symbolSet[i], freqSet[i]);
		total_freq += freqSet[i];
	}

	for (i = 1; i <= n - 1; i++) {
		u = PQ_delete();
		v = PQ_delete();
		w = make_a_new_node();
		w->left = u;
		w->right = v;
		w->freq = u->freq + v->freq;
		PQ_insert(w);
	}
	w = PQ_delete();

	/* w points to the optimal tree. */
	/* find the maximum of tree depth. */
	tree_depth = 0;
	Find_Depth(w, 0, tree_depth);
	
	/* Print the encoding value of char */
	encoding = (char *)malloc((tree_depth + 1));
	printf("--- Print the encoding value of char ---\n\n");
	printf("char | encode | percentage \n");
	Print_Tree(w, 0, encoding);
	printf("\n\n--- Print End ---\n\n");
}

int main()
{
	Huffman_Coding();

	return 0;
}

 

 

'학교에서 들은거 정리 > 알고리즘설계와분석' 카테고리의 다른 글

Scheduling with Deadlines with Disjoint Sets  (0) 2021.12.07
Disjoint Sets  (0) 2021.11.30
Scheduling with Deadlines  (0) 2021.11.25
Scheduling  (0) 2021.11.23
Subset Sum Problem  (0) 2021.11.09
Comments