Kode Huffman itu!

13

Atau dia akan terengah-engah dan menghancurkan rumah Anda!

Itu sama sekali tidak relevan. Tantangan ini sebenarnya tentang pengkodean Huffman . Intinya adalah frekuensi karakter dalam teks yang diberikan digunakan untuk membuat representasi lebih pendek. Dengan kata lain, katakanlah alfabet kita adalah amelalui zdan ruang. Itu 27 karakter. Masing-masing dapat dikodekan secara unik hanya dalam 5 bit karena 5 bit memiliki ruang yang cukup untuk 32 karakter. Namun, dalam banyak situasi (seperti bahasa Inggris, atau bahasa pada umumnya), beberapa karakter lebih sering daripada yang lain. Kita dapat menggunakan bit lebih sedikit untuk karakter yang lebih sering dan (mungkin) lebih banyak bit untuk karakter yang lebih jarang. Dilakukan dengan benar, ada penghematan keseluruhan dalam jumlah bit dan teks asli masih dapat direkonstruksi secara unik.

Mari kita ambil "pertanyaan ini tentang pengodean huffman" sebagai contoh. Teks ini panjangnya 37 karakter, yaitu 37 * 8 = 296 bit secara normal, meskipun hanya 37 * 5 = 185 bit jika kita hanya menggunakan 5 bit untuk setiap karakter. Ingatlah itu.

Berikut adalah tabel (agak) dari setiap karakter dan frekuensi mereka dalam teks, diurutkan dari yang paling sering (di mana _ berarti spasi):

_ 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

Pengkodean optimal terkait dapat:

_ 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

Harus segera jelas bahwa ini akan menjadi pengkodean yang lebih baik daripada hanya menggunakan 5 bit untuk setiap karakter. Mari kita cari tahu seberapa jauh lebih baik!

145 bit , dibandingkan dengan 185! Itu penghematan 40 bit, atau lebih dari 20%! (Ini, tentu saja, dengan asumsi bahwa informasi tentang struktur tersedia untuk decoding.) Pengkodean ini optimal karena tidak ada lagi bit yang bisa dihapus dengan mengubah representasi karakter apa pun.

Tugas

  • Tulis program atau fungsi dengan satu parameter yang ...
  • Mengambil input dari STDIN (atau setara) atau sebagai argumen tunggal.
  • Keluarkan kode Huffman yang optimal seperti di atas dengan karakter diurutkan berdasarkan frekuensi (urutan dalam kelas frekuensi tidak masalah).
  • Anda dapat mengasumsikan bahwa karakter dalam input dibatasi untuk rentang ASCII 32..126plus baris baru.
  • Anda dapat mengasumsikan input tidak lebih dari 10.000 karakter (idealnya, secara teori, input tidak boleh dibatasi).
  • Kode Anda harus selesai dengan cepat. Contoh yang diberikan di atas seharusnya tidak lebih dari satu menit atau lebih buruk. (Ini dimaksudkan untuk mengesampingkan brute force.)
  • Skor dalam byte.

Contohnya

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

Selamat coding!


Perhatikan bahwa pertanyaan serupa ini berkaitan erat, bahkan sampai pada titik bahwa ini adalah duplikat. Namun, konsensus sejauh ini pada Meta adalah bahwa yang lebih tua harus dianggap duplikat dari yang ini.

El'endia Starman
sumber
1
Saya tidak setuju dengan catatan Anda: itu adalah pertanyaan yang sama yang untuk jawaban yang ada memerlukan transformasi sederhana dari format output, dan terlebih lagi jawaban untuk pertanyaan ini secara otomatis merupakan jawaban untuk pertanyaan sebelumnya.
Peter Taylor
@PeterTaylor: Saya ingin mengajukan petisi lagi bahwa Anda membuka kembali pertanyaan ini. Spesifikasi yang satu ini lebih baik (seperti dikatakan Martin) dan saya ingin melihat jawaban yang lebih baru dan lebih baik, termasuk jawaban Pyth dan CJam. Saya pikir tidak ada salahnya membiarkan kedua pertanyaan terbuka karena keduanya cukup berbeda. Hanya dua dari lima pengguna yang memposting pertanyaan itu telah berada di situs ini baru-baru ini.
El'endia Starman
@PeterTaylor: Juga, mengikuti standar ini , saya ingin mengatakan bahwa saya tidak berpikir jawaban bisa disalin antara pertanyaan dan tetap kompetitif. Akhirnya, pertanyaan lainnya adalah empat tahun . Akan lebih baik jika memiliki versi baru.
El'endia Starman
Dalam contoh Anda this question is about huffman coding, saya menghitung jumlah bit menjadi 145 , bukan 136.
TFeld
1
Saya benar-benar mencoba untuk menyelesaikan tantangan ini di Spoon , tetapi setelah 2 jam brainfuckery saya memutuskan akan lebih baik untuk menyerah ...
Bassdrop Cumberwubwubwub

Jawaban:

2

Pyth, 53 byte

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

Demonstrasi

Berikut adalah versi yang menunjukkan keadaan internal, sehingga Anda dapat melihat pengkodean sedang dibangun:

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

Demonstrasi

Salin output ke lingkungan dengan garis yang lebih luas untuk gambar yang lebih jelas.

isaacg
sumber
4

Python 2, 299 byte

Inilah usaha saya untuk menjawab.

Kode Huffman berbeda dari contoh yang diberikan, tetapi harus tetap optimal.

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
TFeld
sumber
2

Matlab, 116 byte

tabulatemembuat tabel frekuensi. huffmandictmengambil daftar simbol dan probabilitas untuk setiap simbol, dan menghitung kodenya.

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
cacat
sumber
2

Rubi, 189 180 byte

Bekerja dalam proses.

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

Ini adalah fungsi anonim; menetapkan itu untuk sesuatu, misalnya f, dan menyebutnya dengan

f["some test string"]`

yang mengembalikan hash seperti ini:

{"t"=>"00", "g"=>"0100", "o"=>"0101", " "=>"011", "e"=>"100", "n"=>"1010", "i"=>"1011", "m"=>"1100", "r"=>"1101", "s"=>"111"}
daniero
sumber
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

Contoh penggunaan:

*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

Bagaimana itu bekerja:

ppanggilan fyang membangun tabel Huffman dalam bentuk daftar (karakter, pengodean) -pairs, misalnya [ ('a',"0"), ('b',"1") ], mengurutkan tabel berdasarkan panjang pengkodean, memformat setiap pasangan untuk output dan bergabung dengan baris baru di antara keduanya.

fpertama-tama periksa satu huruf dan kembalikan tabel yang sesuai. Kalau tidak, ia mengurutkan string input dan mengelompokkan urutan karakter yang sama (misalnya "ababa"-> ["aaa","bb"]) dan memetakannya menjadi pasangan (sequence , [(char, "")]), (-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]. Elemen pertama digunakan untuk melacak frekuensi, elemen kedua adalah daftar pasangan karakter. dan itu encoding (yang awalnya kosong). Ini semua tabel elemen Huffman tunggal seperti yang diharapkan oleh pdan dikombinasikan oleh gdan h.

gmengurutkan daftar pasangan pada panjang elemen pertama, yaitu frekuensi dan panggilan h. hmenggabungkan tabel Huffman dari dua elemen pertama, dengan menggabungkan frekuensi dan menempatkan a 0( 1) di depan setiap elemen dari tabel pertama (kedua). Gabungkan kedua tabel. Panggil glagi, berhenti ketika ada satu elemen yang tersisa, buang bagian frekuensi dan kembalikan tabel Huffman penuh.

nimi
sumber
1

K (ngn / k) , 78 byte

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

Cobalah online!

mengembalikan daftar string untuk dicetak

h::0#'xmembuat daftar kosong untuk setiap karakter (secara teknis, itu membentuk kembali setiap karakter dengan panjang 0). kami akan menyimpan kode huffman terbalik di sana. kami menggunakan ::alih-alih :untuk penugasan untuk menjadikan hglobal sehingga terlihat di sub-fungsi.

.=x adalah daftar daftar - indeks string yang dikelompokkan berdasarkan nilai karakter

(#1_) adalah fungsi yang mengembalikan kebenaran jika panjang argumen adalah> 1 (secara teknis "panjang 1 tetes ...")

(#1_){... }/berarti: sementara argumen memiliki panjang> 1, tetap menerapkan fungsi kurung kurawal

x@<#'x urutkan argumen berdasarkan panjangnya

0 2_ potong menjadi kepala 2-elemen dan ekor

{h[x],:!2;y,,,/x}memperbarui hdengan menambahkan 0 dan 1 ke indeks yang ada di kepala; kembalikan ekor dengan kepala sebagai elemen tunggal

(?,/'x,'" ",'|'$h)(?x)?>#'=xmembalikkan masing-masing h, menyortir, unik, berkorespondensi karakter yang sesuai, dan memformat dengan baik

ngn
sumber
0

JavaScript (ES6) 279

Intinya, algoritma dasar dari Wikipedia. Saya mungkin bisa melakukan yang lebih baik.

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

Lebih mudah dibaca di dalam cuplikan di bawah ini

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>

edc65
sumber