Huffman'ı kodlayın!


13

Yoksa evini şişirir, şişirir ve evini havaya uçurur!

Bu tamamen alakasızdı. Bu zorluk aslında Huffman kodlamasıyla ilgilidir . Bunun özü, temsilini kısaltmak için belirli bir metindeki karakterlerin sıklığıdır. Başka bir deyişle, en bizim alfabe olduğunu varsayalım ayoluyla zve uzay. Bu 27 karakterdir. Her biri sadece 5 bit ile benzersiz şekilde kodlanabilir, çünkü 5 bit 32 karakter için yeterli alana sahiptir. Ancak, çoğu durumda (İngilizce veya genel olarak diller gibi), bazı karakterler diğerlerinden daha sıktır. Biz kullanabilirsiniz az daha seyrek karakterler için daha sık karakterler ve (belki) daha fazla bit için bit. Doğru yapıldığında, bit sayısında genel bir tasarruf vardır ve orijinal metin yine de benzersiz bir şekilde yeniden oluşturulabilir.

Örnek olarak "bu soru huffman kodlamasıyla ilgilidir" örneğini ele alalım. Bu metin 37 karakter uzunluğundadır, normalde 37 * 8 = 296 bit olur, ancak her karakter için yalnızca 5 bit kullanırsak yalnızca 37 * 5 = 185 bit olur. Bunu aklınızda bulundurun.

Her karakterin ve metindeki sıklıklarının, en azdan en az sıklığa göre sıralanmış bir (sorta) tablosu (burada bir boşluk için duruyor):

_ 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

İlişkili bir optimal kodlama şunlar olabilir:

_ 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

Bunun, her karakter için sadece 5 bit kullanmaktan daha iyi bir kodlama olacağı hemen anlaşılmalıdır. Ne kadar iyi olduğunu öğrenelim!

185 bit , 185 ile karşılaştırıldı! Bu 40 bitlik bir tasarruf veya% 20'nin biraz üzerinde! (Bu, elbette, yapı hakkındaki bilgilerin kod çözme için mevcut olduğunu varsayar.) Bu kodlama en uygunudur, çünkü herhangi bir karakterin temsilini değiştirerek daha fazla bit atılamaz.

Görev

  • Bir program veya işlev bir parametre ile yazın ...
  • STDIN'den (veya eşdeğeri) veya tek bir bağımsız değişken olarak girdi alır.
  • Frekansa göre sıralanmış karakterlerle yukarıdaki gibi optimum bir Huffman kodlaması çıktı alın (bir frekans sınıfındaki sıralama önemli değildir).
  • Girişteki karakterlerin ASCII aralığı 32..126artı bir yeni satırla sınırlı olduğunu varsayabilirsiniz .
  • Girdinin 10.000 karakterden uzun olmadığını varsayabilirsiniz (ideal olarak, teoride, girdinin sınırsız olması gerekir).
  • Kodunuzun oldukça hızlı bitmesi gerekir. Yukarıda verilen örnek en az bir dakikadan fazla sürmemelidir. (Bu kaba kuvvetin dışlanması anlamına gelir.)
  • Puanlama bayt cinsindendir.

Örnekler

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

Mutlu kodlama!


Bu benzer sorunun , bunun yinelenen bir soru olduğu noktaya kadar yakından ilişkili olduğunu unutmayın . Bununla birlikte, Meta üzerinde şu ana kadarki konsensüs , daha eski olanın bunun bir kopyası olarak görülmesi gerektiğidir.


1
Notunuza katılmıyorum: mevcut cevaplar için çıktı formatının basit bir şekilde dönüştürülmesini gerektiren aynı soru ve ayrıca bu soruya verilecek herhangi bir cevap otomatik olarak bir önceki soruya cevap.
Peter Taylor

@PeterTaylor: Bu soruyu tekrar açtığınız için tekrar dilekçe vermek istiyorum. Bu spesifikasyon daha iyidir (Martin tarafından söylendiği gibi) ve Pyth ve CJam cevapları dahil olmak üzere daha yeni, daha iyi cevaplar görmek istiyorum. Her iki soruyu da açık bırakmanın bir zararı olmadığını düşünüyorum çünkü yeterince farklılar. Bu soruyu yayınlayan beş kullanıcıdan yalnızca ikisi bu sitede yer almaktadır.
El'endia Starman

@PeterTaylor: Ayrıca, bu standarda göre , cevapların sorular arasında kopyalanabileceğini ve rekabetçi kalabileceğini düşünmediğimi söylemek istiyorum. Son olarak, diğer soru dört yaşında . Yeni bir versiyona sahip olmak iyi olurdu.
El'endia Starman

Sizin örnekte this question is about huffman coding, ben olmak bit sayısını sayılan 145 değil 136.,
TFeld

1
Ben gerçekten Spoon bu meydan okuma tamamlamak için çalışıyordu , ama 2 saat
beyin fırtınası

Yanıtlar:


2

Pyth, 53 bayt

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

gösteri

İşte iç durumu gösteren bir sürüm, böylece kodlamanın yapıldığını görebilirsiniz:

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

gösteri

Daha net bir görüntü için çıkışı daha geniş çizgiler içeren bir ortama kopyalayın.


4

Python 2, 299 bayt

İşte benim bir cevap girişimim.

Huffman kodları verilen örneklerden farklıdır, ancak yine de optimal olmalıdır.

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 bayt

tabulatesıklık tablosu yapar. huffmandicther sembol için sembollerin ve olasılıkların bir listesini alır ve kodu hesaplar.

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

Yakut, 189 180 bayt

Devam eden çalışma.

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

Anonim bir işlevdir; örneğin bir şeye atayın fve

f["some test string"]`

ki bunun gibi bir karma döndürür:

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

1

Haskell, 227 bayt

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

Kullanım örneği:

*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

Nasıl çalışır:

pçağrılar f, örneğin (karakter, kodlama) çiftlerine, bir liste şeklinde Huffman tablosu oluşturur [ ('a',"0"), ('b',"1") ], kodlamaları uzunluğu masa sıralar çıkışı için her çift biçimlendirir ve arasında yeni satır ile birleşir.

fönce tek harfli büyük / küçük harf durumunu kontrol eder ve karşılık gelen tabloyu döndürür. Aksi takdirde, giriş dizesini sıralar ve eşit karakter dizilerini (örn. "ababa"-> ["aaa","bb"]) gruplar ve çiftlerle eşleştirir (sequence , [(char, "")]), (-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]. İlk öğe frekansı izlemek için kullanılır, ikinci öğe bir karakterin çiftlerinin listesidir ve (başlangıçta boş olan) kodlayan var. Bu, beklendiği gibi, tüm tek eleman Huffman tablolardır ptarafından birleştirilir gve h.

gçiftlerin listesini ilk elemanın uzunluğuna, yani frekansa ve çağrılara göre sıralar h. hfrekansları birleştirerek ve ilk (ikinci) tablonun her elemanının önüne bir 0( 1) koyarak ilk iki elemanın Huffman tablolarını birleştirir . Her iki tabloyu birleştirin. gTekrar arayın , tek bir eleman kaldığında durun, frekans kısmını atın ve tam Huffman tablosunu döndürün.


1

K (ngn / k) , 78 bayt

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

Çevrimiçi deneyin!

yazdırmak için bir dize listesi döndürür

h::0#'xher karakter için boş bir liste oluşturur (teknik olarak, her karakteri 0 uzunluğuna göre yeniden şekillendirir). ters huffman kodlarını orada saklayacağız. Kullandığımız ::yerine :yapmak atama için hbu alt fonksiyonlarda görünür yüzden küresel.

.=x bir liste listesidir - karakter değerine göre gruplandırılmış dizenin dizinleri

(#1_) argümanın uzunluğu> 1 ise (teknik olarak "1 damla uzunluk ...") doğrulukla dönen bir işlevdir

(#1_){... şu }/anlama gelir: bağımsız değişken uzunluk> 1 olsa da, küme ayracı işlevini uygulamaya devam edin

x@<#'x argümanı uzunluğa göre sırala

0 2_ 2 elemanlı bir kafaya ve kuyruğa kes

{h[x],:!2;y,,,/x}hkafadaki indekslere 0 ve 1 ekleyerek güncelleme ; kuyruğu kafa ile tek bir eleman olarak geri getirin

(?,/'x,'" ",'|'$h)(?x)?>#'=xher birini tersine çevirin h, sıralayın, benzersiz yapın, karşılık gelen karakterlerin başına ekleyin ve güzel biçimlendirin


0

JavaScript (ES6) 279

Temel olarak, Wikipedia'dan temel algoritma. Muhtemelen daha iyisini yapabilirim.

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

Aşağıdaki snippet'in içinde daha okunabilir

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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.