รหัส Huffman!


13

มิฉะนั้นเขาจะโกรธและพองและระเบิดบ้านของคุณ!

นั่นไม่เกี่ยวข้องอย่างสมบูรณ์ ความท้าทายนี้เป็นจริงเกี่ยวกับHuffman การเข้ารหัส ส่วนสำคัญของมันคือความถี่ของตัวละครในข้อความที่กำหนดจะถูกใช้เพื่อทำให้การแสดงสั้นลง กล่าวอีกนัยหนึ่งสมมติว่าตัวอักษรของเราaผ่านไปzและผ่านช่องว่าง นั่นคือ 27 ตัวอักษร แต่ละรหัสสามารถเข้ารหัสได้ใน 5 บิตเพราะ 5 บิตมีพื้นที่เพียงพอสำหรับ 32 อักขระ อย่างไรก็ตามในหลาย ๆ สถานการณ์ (เช่นภาษาอังกฤษหรือภาษาทั่วไป) ตัวละครบางตัวจะบ่อยกว่าตัวละครอื่น ๆ เราสามารถใช้บิตน้อยลงสำหรับอักขระบ่อยขึ้นและ (อาจ) บิตเพิ่มเติมสำหรับอักขระบ่อยน้อย ถูกต้องแล้วมีการประหยัดโดยรวมในจำนวนบิตและข้อความต้นฉบับยังคงสามารถสร้างขึ้นใหม่ได้โดยไม่ซ้ำกัน

ลอง "คำถามนี้เกี่ยวกับการเข้ารหัส huffman" เป็นตัวอย่าง ข้อความนี้มีความยาว 37 ตัวอักษรซึ่งปกติจะเป็น 37 * 8 = 296 บิตโดยปกติจะมีเพียง 37 * 5 = 185 บิตหากเราใช้ 5 บิตสำหรับแต่ละอักขระเท่านั้น เก็บไว้ในใจ

นี่คือตาราง (sorta) ของตัวละครแต่ละตัวและความถี่ในข้อความเรียงจากมากไปน้อยบ่อย (โดยที่ _ ย่อมาจาก space):

_ 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 (หรือเทียบเท่า) หรือเป็นอาร์กิวเมนต์เดี่ยว
  • เอาท์พุทการเข้ารหัส Huffman ที่ดีที่สุดข้างต้นด้วยตัวอักษรที่เรียงตามความถี่ (ลำดับภายในคลาสความถี่ไม่สำคัญ)
  • คุณอาจสมมติว่าอักขระในอินพุตถูก จำกัด ช่วง ASCII 32..126บวกกับขึ้นบรรทัดใหม่
  • คุณอาจสมมติว่าอินพุตไม่เกิน 10,000 ตัวอักษร (โดยหลักแล้วทฤษฎีควรป้อนข้อมูลไม่ จำกัด )
  • รหัสของคุณควรเสร็จเร็วพอสมควร ตัวอย่างที่ให้มาข้างต้นไม่ควรเกินหนึ่งนาทีหรือแย่ที่สุด (นี่มีวัตถุประสงค์เพื่อแยกแยะกำลังดุร้าย)
  • การให้คะแนนอยู่ในหน่วยไบต์

ตัวอย่าง

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: ฉันต้องการที่จะยื่นคำร้องอีกครั้งว่าคุณเปิดคำถามนี้อีกครั้ง สเปคในอันนี้ดีกว่า (ดังที่มาร์ตินพูด) และฉันต้องการเห็นคำตอบที่ใหม่กว่าดีกว่ารวมถึงคำตอบของ Pyth และ CJam ฉันคิดว่าไม่มีอันตรายใด ๆ ในการเปิดคำถามทั้งสองไว้เพราะมันต่างกันพอสมควร มีผู้ใช้สองในห้าคนเท่านั้นที่โพสต์คำถามนี้บนเว็บไซต์นี้เมื่อไม่นานมานี้
El'endia Starman

@ PeterTaylor: นอกจากนี้ตามมาตรฐานนี้ฉันอยากจะบอกว่าฉันไม่คิดว่าคำตอบสามารถคัดลอกระหว่างคำถามและยังคงแข่งขันได้ สุดท้ายคำถามอื่น ๆ ที่เป็นสี่ปี มันคงจะดีถ้ามีรุ่นใหม่
El'endia Starman

ในตัวอย่างของthis question is about huffman codingคุณฉันนับจำนวนบิตเป็น145ไม่ใช่ 136
TFeld

1
ฉันพยายามทำให้ความท้าทายนี้เสร็จสมบูรณ์ในSpoonแต่หลังจากผ่านไป 2 ชั่วโมงฉันคิดว่ามันเป็นการดีที่สุดที่จะยอมแพ้ ...
Bassdrop Cumberwubwubwub

คำตอบ:


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

Python 2, 299 ไบต์

นี่คือความพยายามของฉันที่คำตอบ

รหัส Huffman นั้นแตกต่างจากตัวอย่างที่ให้มา แต่ก็ควรจะเหมาะสมที่สุด

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

Haskell, 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

มันทำงานอย่างไร:

pสายfที่สร้างตาราง Huffman ในรูปแบบของรายการ (ตัวละคร, การเข้ารหัส) - คู่เช่น[ ('a',"0"), ('b',"1") ]เรียงลำดับตารางตามความยาวของการเข้ารหัสจัดรูปแบบแต่ละคู่สำหรับการส่งออกและเข้าร่วมกับขึ้นบรรทัดใหม่ในระหว่าง

fก่อนตรวจสอบกรณีตัวอักษรเดียวและส่งกลับตารางที่เกี่ยวข้อง มิฉะนั้นจะเรียงลำดับสตริงอินพุตและกลุ่มของอักขระที่เท่ากัน (เช่น"ababa"-> ["aaa","bb"]) และแมปเป็นคู่(sequence , [(char, "")])(-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]องค์ประกอบแรกที่ใช้ในการติดตามความถี่ขององค์ประกอบที่สองคือรายการคู่ของตัวละคร และก็การเข้ารหัส (ซึ่งเป็นครั้งแรกที่ว่างเปล่า). เหล่านี้ทุกตารางธาตุ Huffman เดียวตามความคาดหวังpและจะรวมกันโดยและgh

g เรียงลำดับรายการคู่ตามความยาวขององค์ประกอบแรกเช่นความถี่และการโทร hเรียงลำดับรายชื่อของคู่กับความยาวขององค์ประกอบแรกคือความถี่และการโทรhรวมตาราง Huffman ของสององค์ประกอบแรกเข้าด้วยกันโดยเชื่อมต่อความถี่และวาง0( 1) ด้านหน้าของทุกองค์ประกอบของตาราง (สอง) แรก เชื่อมตารางทั้งสองเข้าด้วยกัน โทรgอีกครั้งหยุดเมื่อมีองค์ประกอบหนึ่งเหลือทิ้งส่วนความถี่แล้วส่งคืนตาราง Huffman แบบเต็ม


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) เราจะเก็บรหัส huffman ที่ตรงกันข้าม เราใช้::แทน:การมอบหมายเพื่อทำให้เป็นhสากลดังนั้นจึงสามารถมองเห็นได้ในฟังก์ชั่นย่อย

.=x คือรายการของรายการ - ดัชนีของสตริงที่จัดกลุ่มตามค่าอักขระ

(#1_) เป็นฟังก์ชั่นที่คืนค่าจริงถ้า if ความยาวของอาร์กิวเมนต์คือ> 1 (ในทางเทคนิค "ความยาว 1 หยดของ ... ")

(#1_){...}/หมายถึง: ในขณะที่การโต้แย้งมีความยาว> 1 ให้ใช้ฟังก์ชั่น curly-brace

x@<#'x จัดเรียงอาร์กิวเมนต์ตามความยาว

0 2_ ตัดมันออกเป็น 2 ส่วนหัวและส่วนท้าย

{h[x],:!2;y,,,/x} ปรับปรุง hโดยผนวก 0 และ 1 เข้ากับดัชนีที่อยู่ในส่วนหัว คืนหางโดยใช้หัวเป็นองค์ประกอบเดียว

(?,/'x,'" ",'|'$h)(?x)?>#'=xย้อนกลับแต่ละรายการhเรียงลำดับไม่ซ้ำกันเติมอักขระที่เกี่ยวข้องและจัดรูปแบบได้อย่างสวยงาม


0

JavaScript (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.