Mã Huffman!


13

Nếu không anh ta sẽ giận dữ và thổi phồng và thổi bay ngôi nhà của bạn!

Điều đó hoàn toàn không liên quan. Thách thức này thực sự là về mã hóa Huffman . Điểm chính của nó là tần số các ký tự trong một văn bản đã cho được sử dụng để làm cho biểu diễn của nó ngắn hơn. Nói cách khác, hãy nói rằng bảng chữ cái của chúng ta axuyên qua zvà không gian. Đó là 27 ký tự. Mỗi trong số chúng có thể được mã hóa duy nhất chỉ trong 5 bit vì 5 bit có đủ chỗ cho 32 ký tự. Tuy nhiên, trong nhiều tình huống (như tiếng Anh hoặc ngôn ngữ nói chung), một số nhân vật thường xuyên hơn những người khác. Chúng ta có thể sử dụng ít bit hơn cho các ký tự thường xuyên hơn và (có lẽ) nhiều bit hơn cho các ký tự ít thường xuyên hơn. Thực hiện đúng, có một khoản tiết kiệm tổng thể về số lượng bit và văn bản gốc vẫn có thể được xây dựng lại một cách duy nhất.

Hãy lấy "câu hỏi này là về mã hóa huffman" làm ví dụ. Văn bản này dài 37 ký tự, sẽ là 37 * 8 = 296 bit thông thường, mặc dù chỉ có 37 * 5 = 185 bit nếu chúng ta chỉ sử dụng 5 bit cho mỗi ký tự. Ghi nhớ nó trong tâm trí.

Dưới đây là bảng (sắp xếp) của mỗi ký tự và tần số của chúng trong văn bản, được sắp xếp từ hầu hết đến ít thường xuyên nhất (trong đó _ đứng trong một khoảng trắng):

_ 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

Một mã hóa tối ưu liên quan có thể là:

_ 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

Cần phải rõ ràng rằng đây sẽ là một mã hóa tốt hơn so với việc chỉ sử dụng 5 bit cho mỗi ký tự. Tuy nhiên, hãy tìm hiểu xem tốt hơn bao nhiêu!

145 bit , so với 185! Đó là một khoản tiết kiệm 40 bit, hoặc chỉ hơn 20%! (Tất nhiên, điều này là giả sử rằng thông tin về cấu trúc có sẵn để giải mã.) Mã hóa này là tối ưu vì không thể bỏ thêm bit nào bằng cách thay đổi bất kỳ biểu diễn nào của ký tự.

Nhiệm vụ

  • Viết chương trình hoặc hàm với một tham số ...
  • Lấy đầu vào từ STDIN (hoặc tương đương) hoặc dưới dạng một đối số.
  • Xuất ra một mã Huffman tối ưu như trên với các ký tự được sắp xếp theo tần số (thứ tự trong một lớp tần số không thành vấn đề).
  • Bạn có thể cho rằng các ký tự trong đầu vào bị giới hạn trong phạm vi ASCII 32..126 cộng với một dòng mới.
  • Bạn có thể giả sử đầu vào không dài hơn 10.000 ký tự (lý tưởng là về lý thuyết, đầu vào không bị ràng buộc).
  • Mã của bạn sẽ hoàn thành hợp lý nhanh chóng. Ví dụ đưa ra ở trên sẽ mất không quá một phút hoặc lâu nhất là tồi tệ nhất. (Điều này nhằm loại trừ lực lượng vũ phu.)
  • Ghi điểm được tính bằng byte.

Ví dụ

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

Chúc mừng mã hóa!


Lưu ý rằng câu hỏi tương tự này có liên quan chặt chẽ, thậm chí đến mức câu hỏi này là trùng lặp. Tuy nhiên, sự đồng thuận cho đến nay về Meta là cái cũ hơn nên được coi là một bản sao của cái này.


1
Tôi không đồng ý với lưu ý của bạn: đó là câu hỏi tương tự mà đối với các câu trả lời hiện tại yêu cầu chuyển đổi đơn giản định dạng đầu ra và hơn nữa, bất kỳ câu trả lời nào cho câu hỏi này đều tự động là câu trả lời cho câu hỏi trước đó.
Peter Taylor

@PeterTaylor: Tôi muốn kiến ​​nghị một lần nữa rằng bạn mở lại câu hỏi này. Thông số kỹ thuật trong phần này là tốt hơn (như Martin đã nói) và tôi muốn xem các câu trả lời mới hơn, tốt hơn, bao gồm các câu trả lời của Pyth và CJam. Tôi nghĩ không có hại gì khi để cả hai câu hỏi mở vì chúng đủ khác nhau. Chỉ có hai trong số năm người dùng đăng lên câu hỏi đó gần đây.
El'endia Starman

@PeterTaylor: Ngoài ra, theo tiêu chuẩn này , tôi muốn nói rằng tôi không nghĩ câu trả lời có thể được sao chép giữa các câu hỏi và vẫn cạnh tranh. Cuối cùng, câu hỏi khác là bốn tuổi . Sẽ tốt hơn nếu có một phiên bản mới.
El'endia Starman

Trong ví dụ của bạn this question is about huffman coding, tôi đã đếm số bit là 145 , không phải 136.
TFeld

1
Tôi đã thực sự cố gắng hoàn thành thử thách này trong Spoon , nhưng sau 2 giờ cân não, tôi quyết định tốt nhất là nên từ bỏ ...
Bassdrop Cumberwubwubwub

Câu trả lời:


2

Bình thường, 53 byte

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

Trình diễn

Đây là phiên bản hiển thị trạng thái bên trong, vì vậy bạn có thể thấy mã hóa đang được xây dựng:

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

Trình diễn

Sao chép đầu ra vào một môi trường với các đường rộng hơn để có hình ảnh rõ hơn.


4

Python 2, 299 byte

Đây là nỗ lực của tôi tại một câu trả lời.

Các mã Huffman khác với các ví dụ đã cho, nhưng vẫn phải tối ưu.

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 byte

tabulatelàm bảng tần số. huffmandictlấy một danh sách các ký hiệu và xác suất cho mỗi ký hiệu và tính toán mã.

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

Hồng ngọc 189 180 byte

Công việc đang tiến triển.

->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}

Đó là một chức năng ẩn danh; gán nó cho một cái gì đó, ví dụ f, và gọi nó với

f["some test string"]`

trong đó trả về một hàm băm như thế này:

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

1

Haskell, 227 byte

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

Ví dụ sử dụng:

*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

Làm thế nào nó hoạt động:

pcác lệnh gọi fxây dựng bảng Huffman dưới dạng một danh sách các ký tự (ký tự, mã hóa), ví dụ: [ ('a',"0"), ('b',"1") ]sắp xếp bảng theo độ dài mã hóa, định dạng mỗi cặp cho đầu ra và nối với các dòng mới ở giữa.

fđầu tiên kiểm tra trường hợp chữ cái duy nhất và trả về bảng tương ứng. Mặt khác, nó sắp xếp chuỗi đầu vào và các chuỗi các ký tự bằng nhau (ví dụ "ababa"-> ["aaa","bb"]) và ánh xạ chúng thành các cặp (sequence , [(char, "")]), (-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]. Phần tử đầu tiên được sử dụng để theo dõi tần số, phần tử thứ hai là danh sách các cặp ký tự và nó mã hóa (ban đầu trống). Đây là tất cả các bảng Huffman phần tử duy nhất như mong đợi pvà được kết hợp bởi gh.

gsắp xếp danh sách các cặp theo độ dài của phần tử đầu tiên, tức là tần số và các cuộc gọi h. hkết hợp các bảng Huffman của hai phần tử đầu tiên, bằng cách ghép các tần số và đặt một 0( 1) trước mọi phần tử của bảng thứ nhất (thứ hai). Nối cả hai bảng. Gọi glại, dừng lại khi có một phần tử duy nhất còn lại, vứt bỏ phần tần số và trả lại bảng Huffman đầy đủ.


1

K (ngn / k) , 78 byte

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

Hãy thử trực tuyến!

trả về một danh sách các chuỗi để in

h::0#'xtạo một danh sách trống cho mỗi ký tự (về mặt kỹ thuật, nó sẽ định hình lại mỗi ký tự thành độ dài 0). chúng tôi sẽ lưu trữ mã huffman đảo ngược ở đó. chúng tôi sử dụng ::thay vì :gán để làm cho htoàn cầu để nó hiển thị trong các chức năng phụ.

.=x là danh sách các danh sách - các chỉ số của chuỗi được nhóm theo giá trị ký tự

(#1_) là một hàm trả về giá trị trung thực nếu độ dài của đối số là> 1 (về mặt kỹ thuật là "độ dài của 1 giọt ...")

(#1_){... }/có nghĩa là: trong khi đối số có độ dài> 1, hãy tiếp tục áp dụng hàm xoăn

x@<#'x sắp xếp đối số theo độ dài

0 2_ cắt nó thành một đầu 2 yếu tố và một cái đuôi

{h[x],:!2;y,,,/x}cập nhật hbằng cách nối 0 và 1 vào các chỉ số có trong phần đầu; trả lại cái đuôi với cái đầu như một yếu tố duy nhất

(?,/'x,'" ",'|'$h)(?x)?>#'=xđảo ngược từng h, sắp xếp, duy nhất, thêm các ký tự tương ứng và định dạng độc đáo


0

JavaScript (ES6) 279

Về cơ bản, thuật toán cơ bản từ Wikipedia. Tôi có lẽ có thể làm tốt hơn.

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)}

Dễ đọc hơn bên trong đoạn trích dưới đây

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>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.