허프만 코딩!


13

그렇지 않으면 그는 허풍과 퍼프를하고 집을 날려 버릴 것입니다!

그것은 전혀 관련이 없습니다. 이 과제는 실제로 허프만 코딩 에 관한 것 입니다. 그것의 요지는 주어진 텍스트에서 문자의 빈도가 표현을 더 짧게 만드는 데 사용됩니다. 다시 말해, 알파벳이 a통과 z하고 공간 이라고 가정 해 봅시다 . 27 자입니다. 5 비트는 32자를 수용 할 수있는 공간이 충분하기 때문에 각각 5 비트만으로 고유하게 인코딩 할 수 있습니다. 그러나 영어 나 일반적인 언어와 같은 여러 상황에서 일부 문자는 다른 문자보다 더 자주 사용됩니다. 보다 빈번한 문자에는 더 적은 비트를 사용하고 빈번한 문자에는 더 많은 비트를 사용할 수 있습니다 . 그렇습니다. 비트 수는 전체적으로 절약되며 원본 텍스트는 여전히 고유하게 재구성 될 수 있습니다.

"이 질문은 허프만 코딩에 관한 것"을 예로 들어 봅시다. 이 텍스트의 길이는 37 자이며 일반적으로 37 * 8 = 296 비트이지만 각 문자에 5 비트 만 사용하는 경우 37 * 5 = 185 비트 입니다. 명심하십시오.

다음은 텍스트에서 각 문자와 그 빈도를 나타내는 (소르 타) 표입니다. 가장 빈번하지 않은 순서로 정렬됩니다 (_는 공백을 나타냅니다).

_ 5
i 4
n 3
o 3
s 3
t 3
u 3
a 2
f 2
h 2
b 1
c 1
d 1
e 1
g 1
m 1
q 1

관련된 최적 코딩은 다음과 같습니다.

_ 101
i 011
n 1100
o 1101
s 1110
t 1111
u 001
a 10011
f 0001
h 0101
b 00000
c 00001
d 01000
e 01001
g 10000
m 10001
q 10010

이것이 모든 문자에 대해 5 비트를 사용하는 것보다 더 나은 인코딩이 될 것임을 즉시 분명해야합니다. 그래도 얼마나 나아 졌는지 알아 봅시다!

145 비트 , 185와 비교! 40 비트나 20 % 이상을 절약 할 수 있습니다! (물론 구조에 대한 정보를 디코딩 할 수 있다고 가정합니다.)이 코딩은 문자 표현을 변경하여 더 이상 비트를 삭제할 수 없기 때문에 최적입니다.

작업

  • 하나의 매개 변수로 프로그램이나 함수를 작성하십시오 ...
  • STDIN (또는 동등한) 또는 단일 인수로 입력을받습니다.
  • 빈도별로 정렬 된 문자를 사용하여 위와 같이 최적의 허프만 코딩을 출력합니다 (주파수 클래스 내 순서는 중요하지 않음).
  • 입력의 문자가 ASCII 범위 32..126와 줄 바꾸기 로 제한되어 있다고 가정 할 수 있습니다 .
  • 입력이 10,000자를 넘지 않는다고 가정 할 수 있습니다. 이론적으로 입력은 제한이 없습니다.
  • 코드는 합리적으로 빨리 완료되어야합니다. 위의 예는 최악의 경우 1 분 정도 소요됩니다. (이것은 무차별 대치를 배제하기위한 것입니다.)
  • 점수는 바이트 단위입니다.

x
---
x 0

xxxxxxxxx
---
x 0

xxxxxxxxy
---
x 0
y 1 (these may be swapped)

xxxxxyyyz
---
x 0
y 10
z 11

uuvvwwxxyyzz
---   (or) 
u 000      000
v 001      001
w 100      010
x 101      011
y 01       10
z 11       11

this question is about huffman coding
---
  101
i 011
n 1100
o 1101
s 1110
t 1111
u 001
a 10011
f 0001
h 0101
b 00000
c 00001
d 01000
e 01001
g 10000
m 10001
q 10010

행복한 코딩!


참고 이 비슷한 질문이 밀접하게이 하나가 중복 심지어 점에 관련이있다. 그러나 지금까지 Meta에 대한 합의 는 오래된 것이이 복제본으로 간주되어야한다는 것입니다.


1
귀하의 의견에 동의하지 않습니다. 기존 답변에 대해 간단한 출력 형식 변환이 필요한 것과 동일한 질문이며,이 질문에 대한 답변은 자동으로 이전 질문에 대한 답변입니다.
Peter Taylor

@PeterTaylor :이 질문을 다시 열도록 다시 탄원하고 싶습니다. 이 사양은 Martin이 말한 것처럼 더 좋으며 Pyth 및 CJam 답변을 포함하여 더 새롭고 더 나은 답변을보고 싶습니다. 두 질문이 충분히 다르기 때문에 두 질문을 모두 열어두면 아무런 해가 없다고 생각합니다. 해당 질문에 게시 한 5 명의 사용자 중 2 명만이 최근이 사이트에 가입했습니다.
El'endia Starman

@ PeterTaylor : 또한 이 표준 에 따라 질문 사이에 답변이 복사 될 수 있다고 생각하지 않고 경쟁력을 유지하고 싶습니다. 마지막으로, 다른 질문은 4 살 입니다. 최신 버전을 사용하는 것이 좋습니다.
El'endia Starman

의 예에서 this question is about huffman coding비트 수는 136이 아닌 145로 계산했습니다.
TFeld

1
나는 실제로 Spoon 에서이 도전을 완수하려고 했지만, 2 시간 동안의
성행위 후에

답변:


2

Pyth, 53 바이트

jo_/zhNee.WtHa>JohNZ2+shKC<J2]s.b+RYNeKU2m,/zd]+d\ {z

데모

내부 상태를 보여주는 버전이 있으므로 인코딩이 작성되는 것을 볼 수 있습니다.

jo_/zhNee.WtHvp+`a>JohNZ2+shKC<J2]s.b+RYNeKU2bm,/zd]+d\ {z

데모

보다 선명한 그림을 위해 출력을 더 넓은 선이있는 환경에 복사하십시오.


4

파이썬 2, 299 바이트

여기에 대한 대답이 있습니다.

허프만 코드는 주어진 예제와 다르지만 여전히 최적이어야합니다.

i=raw_input();m=n=[(c,i.count(c))for c in set(i)]
while n[1:]:n.sort(key=lambda x:(x[1]));(a,b),(c,d)=n[:2];n=[((a,c),b+d)]+n[2:]
n=n[0][0]
r=[]
def a(b,s):
 if b[1:]:a(b[0],s+'0');a(b[1],s+'1')
 else:r.append(b+(s if s[1:]else s+'0'))
a(n,' ')
for y in sorted(r,key=lambda x:-dict(m)[x[0]]):print y

2

Matlab, 116 바이트

tabulate빈도 표를 만듭니다. huffmandict각 기호에 대한 기호 및 확률 목록을 가져 와서 코드를 계산합니다.

t=tabulate(input('')');
d=huffmandict(t(:,1),cell2mat(t(:,3))/100);
for i=1:size(d,1);disp([d{i,1},' ',d{i,2}+48]);end

2

루비, 189 180 바이트

진행중인 작업.

->s{m=s.chars.uniq.map{|c|[c,s.count(c)]}
while m[1]
(a,x),(b,y),*m=m.sort_by &:last
m<<[[a,b],x+y]
end
h={}
f=->q="",c{Array===c&&f[q+?0,c[0]]&&f[q+?1,c[1]]||h[c]=q}
f[m[0][0]]
h}

익명의 기능입니다. 예를 들어, 뭔가에 할당 f하고, 그것을 호출

f["some test string"]`

다음과 같은 해시를 반환합니다.

{"t"=>"00", "g"=>"0100", "o"=>"0101", " "=>"011", "e"=>"100", "n"=>"1010", "i"=>"1011", "m"=>"1100", "r"=>"1101", "s"=>"111"}

1

하스켈, 227 바이트

import Data.List
s=sortOn.(length.)
f x|[c]<-nub x=[(c,"0")]|1<2=g[(a,[(a!!0,"")])|a<-group$sort x]
g=h.s fst
h[x]=snd x
h((a,b):(c,d):e)=g$(a++c,map('0'#)b++map('1'#)d):e
n#(a,b)=(a,n:b)
p=unlines.map(\(a,b)->a:" "++b).s snd.f

사용 예 :

*Main> putStr $ p "this question is about huffman coding"
u 000
i 011
  101
a 0010
f 0011
h 1000
s 1100
t 1101
n 1110
o 1111
d 01000
e 01001
b 01010
c 01011
q 10010
g 100110
m 100111

작동 방식 :

pf(문자, 인코딩) 쌍의 목록 형식으로 허프만 테이블을 작성하는 호출 은, 예를 들어 [ ('a',"0"), ('b',"1") ], 인코딩의 길이에 따라 테이블을 정렬하고, 출력을 위해 각 쌍을 포맷하고 그 사이의 개행과 조인합니다.

f먼저 소문자를 확인하고 해당 테이블을 반환합니다. 그렇지 않으면 입력 문자열을 정렬하고 동일한 문자 (예 : "ababa"-> ["aaa","bb"])의 시퀀스를 그룹화 하여 쌍으로 매핑합니다 (sequence , [(char, "")])(-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]. 첫 번째 요소는 빈도를 추적하는 데 사용되며 두 번째 요소는 문자 쌍의 목록입니다 그리고 (처음에 비어있는) 인코딩입니다. 다음은 예상 모든 단일 요소 하프 맨 테이블입니다 p에 의해 결합 g하고 h.

g첫 번째 요소의 길이, 즉 frequency 및 calls에서 쌍 목록을 정렬합니다 h. h는 주파수를 연결 하고 첫 번째 (두 번째) 테이블의 모든 요소 앞에 0( 1)를 넣어 첫 두 요소의 허프만 테이블을 결합 합니다. 두 테이블을 연결하십시오. g다시 전화를 걸어 단일 요소가 남았을 때 멈추고 주파수 부분을 버리고 전체 허프만 테이블을 반환하십시오.


1

K (ngn / k) , 78 바이트

{h::0#'x;(#1_){{h[x],:!2;y,,,/x}.0 2_x@<#'x}/.=x;(?,/'x,'" ",'|'$h)(?x)?>#'=x}

온라인으로 사용해보십시오!

인쇄 할 문자열 목록을 반환합니다.

h::0#'x각 문자에 대해 빈 목록을 만듭니다 (기술적으로는 각 문자를 길이 0으로 바꿉니다). 반전 된 허프만 코드를 저장합니다. 우리 는 전역 을 만들기 위해 할당 ::대신 에 하위 기능에서 볼 수 있도록 사용합니다.:h

.=x 문자 목록으로 그룹화 된 문자열의 색인-목록의 목록입니다.

(#1_) 인수의 길이가> 1 인 경우 (기술적으로 "길이 1 방울의 길이") 진실을 반환하는 함수입니다.

(#1_){... }/의미 : 인수의 길이가 1보다 크지 만 중괄호 함수를 계속 적용

x@<#'x 인수를 길이별로 정렬

0 2_ 2 요소 머리와 꼬리로 자르세요

{h[x],:!2;y,,,/x}h헤드에 포함 된 인덱스에 0과 1을 추가하여 업데이트 합니다. 머리를 가진 꼬리를 단일 요소로 반환

(?,/'x,'" ",'|'$h)(?x)?>#'=x각각의 h문자를 뒤집고 , 정렬하고, 고유하며, 해당 문자를 앞에 붙이고 멋지게 형식화하십시오.


0

자바 스크립트 (ES6) 279

기본적으로 Wikipedia의 기본 알고리즘입니다. 아마 더 잘할 수 있습니다.

f=s=>{for(n=[...new Set(s)].map(c=>({c:c,f:[...s].filter(x=>x==c).length}));n[1];n.push({l:a=n.pop(),r:b=n.pop(),f:a.f+b.f,c:a.c+b.c}))n.sort((a,b)=>b.f-a.f);t=(n,b)=>(n.b=b,n.r)?(t(n.l,b+0),t(n.r,b+1)):o.push(n);t(n[0],'',o=[]);return o.sort((a,b)=>b.f-a.f).map(x=>x.c+' '+x.b)}

아래 스 니펫 내부에서 더 읽기

f=s=>{
  for(n=[...new Set(s)].map(c=>({c:c,f:[...s].filter(x=>x==c).length}));
      n[1];
      n.push({l:a=n.pop(),r:b=n.pop(),f:a.f+b.f,c:a.c+b.c}))
    n.sort((a,b)=>b.f-a.f);
  t=(n,b)=>(n.b=b,n.r)?(t(n.l,b+0),t(n.r,b+1)):o.push(n);
  t(n[0],'',o=[]);
  return o.sort((a,b)=>b.f-a.f).map(x=>x.c+' '+x.b)
}

//TEST
console.log=x=>O.innerHTML+=x+'\n'

test=['xxxxxxxxy','uuvvwwxxyyzz','this question is about huffman coding']
.forEach(t=>console.log(t+'\n'+f(t).join`\n`+'\n'))
<pre id=O></pre>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.