Huffman Encoding
2020-1νκΈ°, λνμμ βμκ³ λ¦¬μ¦β μμ μ λ£κ³ 곡λΆν λ°λ₯Ό μ 리ν κΈμ λλ€. μ§μ μ μΈμ λ νμμ λλ€ :)
μμ½κ²λ <Huffman Encoding>μ λν λ°±μ€ λ¬Έμ λ₯Ό μ°Ύμ§ λͺ» νλ€. κ·Έλμ μ체μ μΌλ‘ λ¬Έμ μ ν μ€νΈ μΌμ΄μ€λ₯Ό κ°λ¨νκ² λ§λ€μ΄ μ΄κ²μ κΈ°μ€μΌλ‘ λ¬Έμλ₯Ό μμ±νλλ‘ νκ² λ€ π
Situation
Consider a string consisting of 130 million characters and the alphabet {A, B, C, D}. CDABDBACCDDBBCCDCC...
How to write this long string in binary with most economic way?
* Input
$1 \le N \le 20$ length alphabetical string.
* Output
Most economic binary encoding.
* Testcase 1
# input
AAAABBBCCD
# output
0000101010111111110
* Testcase 2
# input
ABBCCCBBA
# output
10001111110010
β» NOTE: Several optimal encodings may exist.
μ¬μ€ μλμ μ¬μ΄νΈμμ λͺκ°μ§ ν μ€νΈμΌμ΄μ€λ₯Ό μ§μ λ§λ€μ΄μ νμΈν μ μλ€.
π Huffman Coding
<Huffman Encoding>μ λ¬Έμμ΄μ μμΆνλ λνμ μΈ μΈμ½λ© λ°©μμ΄λ€. κ°λ³ κΈΈμ΄ μΈμ½λ©(variable-length encoding) λ°©μμΈλ° κ° λ¬Έμμ μΆν λΉλμ λ°λΌ μμ£Ό μΆννλ λ¬ΈμλΌλ©΄ λ 짧μ μ½λλ₯Ό λΆμ¬νλ€.
κ³ μ κΈΈμ΄ μΈμ½λ© λ°©μκ³Ό κ°λ³ κΈΈμ΄ μΈμ½λ© λ°©μμ νλ² λΉκ΅ν΄λ³΄μ.
1. κ³ μ κΈΈμ΄ μΈμ½λ©
A = 00, B = 01, C = 10, D = 11
AAAABBBCCD -> 0000 0000 0101 0110 1011 (20 bits)
2. κ°λ³ κΈΈμ΄ μΈμ½λ©
A = 0, B = 10, C = 111, D = 110
AAAABBBCCD -> 0000 1010 1011 1111 110 (19 bits)
μβ¦ μ¬μ€ μμ μμ μμλ κ·Έλ κ² λ§μ΄ μ€μ§ μμμ§λ§, λ§μ½ νΉμ λ¬Έμμ μΆν λΉλκ° λ€λ₯Έ λ¬Έμλ³΄λ€ μλμ μΌλ‘ λ§λ€λ©΄ κ°λ³ κΈΈμ΄ μΈμ½λ©μ μ’μ μ νμ΄ λ μ μλ€! (μλ₯Ό λ€μ΄ μμ΄μμλ λͺ¨μμ ν΄λΉνλ a, i, u, e, o λ¬Έμκ° μμ£Ό λ±μ₯ν κ²μ΄λ€.)
κ·Έλ¬λ μ΄λ° <κ°λ³ κΈΈμ΄ μΈμ½λ©> λ°©μμ encoding-decodingμ΄ μ μΌ(unique)ν΄μΌ νλ€λ 쑰건μ λ§μ‘±ν΄μΌ νλ€! μ°λ¦¬κ° μνΈν(encoding) νμ§λ§ μ νν 볡νΈν(decoding)νμ§ λͺ»νλ€λ©΄ κ·Έκ²μ μ ν λμμ΄ λμ§ μλλ€. <Huffman Encoding>μμλ μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ prefix-free λ°©μμΌλ‘ λ¬Έμλ₯Ό μΈμ½λ© νλ€. μ΄κ²μ μ΄λ€ μΈμ½λ© μ½λλΌλ λ€λ₯Έ μΈμ½λμ prefixκ° λμ§ μλλ€λ λ§μ΄λ€. μλ₯Ό λ€μ΄ Aλ₯Ό 101μΌλ‘ μΈμ½λ© νλ€λ©΄, 1, 10, 101λ‘λ μ λ μΈμ½λ© νμ§ μλλ€λ κ·μΉμ΄λ€.
<Huffman Encoding>μ μλμ κ°μ μκ³ λ¦¬μ¦μΌλ‘ μ΅μ μ μΈμ½λ© λ°©μμ μ°Ύλλ€.
1. λ¬Έμμ΄μμ λ¬Έμλ€μ λΉλμλ₯Ό κ³μ°νλ€: $\{f_i \mid i \in \Omega \}$
2. λΉλμ $f_i$λ₯Ό κΈ°μ€μΌλ‘ λ¬Έμ μ§ν©μ μ λ ¬νλ€.
3. μ λ ¬λ λ¬Έμ λ°°μ΄μμ κ°μ₯ μμ λΉλλ₯Ό κ°μ§λ 2κ° λ¬Έμλ₯Ό κΊΌλ΄ κ°κ°μ left child, right childλ‘ κ°λ νΈλ¦¬λ₯Ό μμ±νλ€. κ·Έλ¦¬κ³ λ νΈλ¦¬μ λΉλμλ₯Ό ν©μΉ λ Έλλ₯Ό λ€μ λ¬Έμ λ°°μ΄μ μ½μ νλ€.
4. [2-3] κ³Όμ μ λ¬Έμ λ°°μ΄μ λ¨ νλμ λ Έλλ§ λ¨μ λκΉμ§ λ°λ³΅νλ€.
μμ κ°μ μκ³ λ¦¬μ¦μ ꡬννκΈ° μν΄ <μ°μ μμ ν>λ₯Ό μ¬μ©νλ€. μ΄ μλ£κ΅¬μ‘°λ₯Ό μ¬μ©νλ©΄ [2] κ³Όμ μ μ λ ¬μ μ§μ μννμ§ μμλ λλ€! κ·Έλμ μκ³ λ¦¬μ¦μ λ€μ μ°λ©΄,
1. λ¬Έμμ΄μμ λ¬Έμλ€μ λΉλμλ₯Ό κ³μ°νλ€: $\{f_i \mid i \in \Omega \}$
2. insert all $f_i$ into a priority queue $H$
3. repeat this until there left one node in $H$
3-1. $u = \texttt{deletemin}(H)$, $v = \texttt{deletemin}(H)$
3-2. create a node $k$ with children $u$ and $v$ and $f_k = f_u + f_v$
3-3. insert new node into $H$
ꡬν
<Huffman Encoding>μ C++λ‘ κ΅¬νν΄λ³΄μ!
λ¬Έμμ΄μ μ λ ₯λ°κ³ , λΉλ μμ λν Mapμ μλμ κ°μ΄ λ§λ€ μ μλ€.
string ss;
cin >> ss; // Assumption. only uppercase Alphabet
// generate frequency map
map<char, int> freq_map;
for (int i = 0; i < ss.length(); i++) {
if (freq_map[ss[i]]) {
freq_map[ss[i]] += 1;
} else {
freq_map[ss[i]] = 1;
}
}
λ€μμ Priority Queue $H$μ λ
Έλλ₯Ό λ£λ κ²μΈλ°, κ·Έ μ μ Node
μλ£κ΅¬μ‘°λ₯Ό μ§μ μ μν΄μΌ νλ€.
struct Node {
int freq;
char character;
Node *left;
Node *right;
};
κ·Έλ¦¬κ³ PQ $H$μ μ°κΈ° μν΄ λΉκ΅μ°μ°μλ μ§μ ꡬνν΄μ€λ€.
struct Node_greater {
bool operator()(const Node *left, const Node *right) {
return left->freq > right->freq;
}
};
κ·Έλ¦¬κ³ PQ $H$λ₯Ό μμ±νλ©΄,
// insert all nodes into priority queue
priority_queue<Node *, vector<Node *>, Node_greater> pq;
for (auto &freq_pair: freq_map) {
char character = freq_pair.first;
int freq = freq_pair.second;
Node *node = new Node({freq, character, NULL, NULL});
pq.push(node);
}
μ²μμλ priorirty_queue<Node, ...>
λ‘ κ΅¬ννλλ° μ£Όμ ν λΉμμ λκ° κΌ¬μ΄λ λλμ΄ μμ΄μ λμ ν λΉκ³Ό priority_queue<Node *, ...>
λ₯Ό μ°λ λ°©ν₯μΌλ‘ ꡬννλ€.
λ€μμ PQ $H$μμ λ Έλλ₯Ό 2κ°μ© κΊΌλ΄μ Huffman Treeλ₯Ό μμ±νλ©΄ λλ€.
// generate Huffman tree
while (pq.size() > 1) {
Node *node1 = pq.top();
pq.pop();
Node *node2 = pq.top();
pq.pop();
Node *newNode = new Node({node1->freq + node2->freq, '0', node1, node2});
pq.push(newNode);
}
κ·Έλ¬λ μ€μ λ‘ λ¬Έμλ₯Ό μΈμ½λ© νκΈ° μν΄μ μμ κ³Όμ μΌλ‘ μ»μ PQ $H$μ μ μ₯λ λ§μ§λ§ λ ΈλμΈ root λ Έλλ₯Ό μ¬μ©ν΄ Huffman Treeλ₯Ό μνν΄μΌ νλ€. μ΄κ²μ DFSλ‘ μλμ κ°μ΄ ꡬνν μ μλ€.
void dfs(map<char, string> &encode_map, const Node &node, string encode) {
if (!node.left && !node.right) {
encode_map[node.character] = encode;
} else {
dfs(encode_map, *node.left, encode + "0");
dfs(encode_map, *node.right, encode + "1");
}
}
...
// encoding using Huffman tree!
Node *root = pq.top();
map<char, string> encode_map;
dfs(encode_map, *root, "");
for (auto &encode: encode_map) {
cout << encode.first << ": " << encode.second << "\n";
}
μμ κ°μ΄ μ ꡬννλ©΄ κ²°κ³Όλ μλμ κ°λ€.
# Testcase 1
(A 4) (B 3) (C 2) (D 1)
A: 0
B: 10
C: 111
D: 110
# Testcase 2
ABBCCCBBA
(A 2) (B 4) (C 3)
A: 10
B: 0
C: 11
λ³Έ ν¬μ€νΈλ <Greedy Algorithm> μ리μ¦μ μΌλΆ μ λλ€. λ λ§μ <Greedy Algorithm>μ λν΄ μ΄ν΄λ³΄κ³ μΆλ€λ©΄ μλμμ νμΈν μ μμ΅λλ€ π
π Greedy Algorithm