Loading [MathJax]/jax/output/CommonHTML/jax.js
💕
후원
본문 바로가기
Algorithm/Algorithm

[알고리즘] 허프만 코딩

by r4bb1t 2021. 12. 16.
반응형

허프만 코딩

원본 데이터에서 자주 출현하는 문자를 적은 비트, 출현 빈도가 낮은 문자를 많은 비트로 부호화해 데이터를 효과적으로 압축하는 방법입니다.

Cn개의 문자로 이루어진 집합으로 두고 cC, c.freq=c의 빈도 수라고 둡니다. Q라는 freq가 key인 최소 우선순위 큐도 둡니다.

HUFFMAN(C)
n = |C| // n은 C의 크기
Q = C // Q를 C에 대해 초기화
for i=1 to n-1
let z = new node; // 새로운 노드 z 할당
z.left = x = EXTRACT_MIN(Q) // Q에서 최소 노드를 뽑아 x, z.left에 할당
z.right = y = EXTRACT_MIN(Q) // Q에서 최소 노드를 뽑아 y, z.right에 할당
z.freq = x.freq + y.freq // Q에서 freq이 최소인 원소 두 개를 빼 새로운 노드의 자식으로 두고, 새로운 노드의 freq를 두 노드의 freq의 합으로 함
INSERT(Q, z) // Q에 z를 삽입함
return EXTRACT_MIN(Q) // 루트 노드 반환

dT(a)a의 트리 T에서의 깊이, B(T)는 트리의 비용을 의미합니다.

보조 정리 1

x,y가 가장 낮은 빈도 수를 갖는 2개의 C의 문자일 때, xy에 대한 허프만 코드는 길이가 같고 마지막 비트만 다릅니다.

 

이를 증명하기 위해 T라는 C에 대한 임의의 최적 prefix 코드를 나타내는 트리를 생각해봅니다. 이 T에서 가장 큰 깊이를 가진 sibling을 a,b라고 둡니다. (a.freqb.freq,x.freqy.freq) 그러면 x.freqa.freq,y.freqb.freq입니다. 이후 T에서 ax, by의 위치를 교환합니다. 그러면 x,y가 최대 깊이의 sibling이 됩니다.

교환 비용(x,y)=x.freqdT(x)+a.freqdT(a)x.freqdt(x)a.freqdT(a)=(a.freqx.freq)(dT(a)dT(x))>0입니다. a.freqx.freq이고 dT(a)dT(x)이기 때문입니다. (b,y)에 대해서도 마찬가지입니다. 따라서 B(T)B(T)이고, 전제에서 T가 최적의 prefix 트리이므로 B(T)B(T), 따라서 B(T)x,y를 최대 깊이의 sibling으로 갖는 최적 prefix 트리입니다.

 

보조 정리 2

CC에서 최소 빈도수를 가진 두 문자 x,y를 제거하고 z.freq=x.freq+y.freq인 문자 z를 추가한 집합이라고 둡니다. T라는 트리를 이 C에 대한 최적의 prefix 트리라고 합시다. 이 때 T에서 z에 해당하는 노드를 x,y를 자식으로 가진 노드로 교체한 트리 TC에 대한 최적의 prefix 트리입니다.

 

cC{x,y}에 대해 dT(C)=dT(C)입니다. 또, dT(x)=dT(y)=dT(z)+1임은 자명합니다. 따라서 x.freqdT(x)+y.freqdT(y)=(x.freq+y.freq)(dT(z)+1)=z.freqdT(z)+x.freq+y.freq입니다. B(T)=B(T)+x.freq+y.freq인 것이죠. 이 때 만약 TC에 대한 최적 prefix 트리가 아니라고 가정해봅시다. xy를 sibling으로 갖는 B(T)<B(T)인 트리 T가 존재한다는 것입니다. 이 T에서 xy의 공통 부모가 z.freq=x.freq+y.freq인 노드 z로 교체된 새로운 트리인 T를 생각해보면, B(T)=B(T)x.freqy.freq<B(T)x.freqy.freq<B(T)인 것인데요. B(T)C에 대한 최적 prefix 트리이므로 모순이 생깁니다. 따라서 TC에 대한 최적 prefix 트리입니다.

반응형

댓글