Code der Huffman!


13

Sonst wird er schnauben und pusten und dein Haus in die Luft jagen!

Das war völlig irrelevant. Bei dieser Herausforderung geht es eigentlich um Huffman-Codierung . Das Wesentliche ist, dass die Häufigkeit der Zeichen in einem bestimmten Text verwendet wird, um seine Darstellung zu verkürzen. Mit anderen Worten, lassen Sie uns sagen, dass unser Alphabet adurch zund Raum ist. Das sind 27 Zeichen. Jedes von ihnen kann in nur 5 Bits eindeutig codiert werden, da 5 Bits genug Platz für 32 Zeichen haben. In vielen Situationen (z. B. Englisch oder Sprachen im Allgemeinen) sind einige Zeichen jedoch häufiger als andere. Wir können weniger Bits für die häufigeren Zeichen und (vielleicht) mehr Bits für die weniger häufigen Zeichen verwenden. Richtig gemacht, ergibt sich eine Gesamtersparnis bei der Anzahl der Bits, und der ursprüngliche Text kann immer noch eindeutig rekonstruiert werden.

Nehmen wir als Beispiel "Diese Frage handelt von Huffman-Codierung". Dieser Text ist 37 Zeichen lang, was normalerweise 37 * 8 = 296 Bit wäre, obwohl nur 37 * 5 = 185 Bit, wenn wir nur 5 Bit für jedes Zeichen verwenden. Merk dir das.

Hier ist eine (sorta) Tabelle der einzelnen Zeichen und ihrer Häufigkeiten im Text, sortiert von am häufigsten bis am seltensten (wobei _ für ein Leerzeichen steht):

_ 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

Eine damit verbundene optimale Kodierung könnte sein:

_ 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

Es sollte sofort klar sein, dass dies eine bessere Codierung ist, als nur 5 Bits für jedes Zeichen zu verwenden. Lassen Sie uns herausfinden, wie viel besser!

145 Bits , verglichen mit 185! Das ist eine Ersparnis von 40 Bit oder etwas mehr als 20%! (Dies setzt natürlich voraus, dass Informationen über die Struktur zum Decodieren verfügbar sind.) Diese Codierung ist optimal, da durch Ändern der Zeichendarstellung keine Bits mehr gelöscht werden können.

Die Aufgabe

  • Schreiben Sie ein Programm oder eine Funktion mit einem Parameter, der ...
  • Übernimmt die Eingabe von STDIN (oder einer Entsprechung) oder als einzelnes Argument.
  • Geben Sie eine optimale Huffman-Codierung wie oben mit den nach Häufigkeit sortierten Zeichen aus (die Reihenfolge innerhalb einer Häufigkeit spielt keine Rolle).
  • Sie können davon ausgehen, dass die Zeichen in der Eingabe auf den ASCII-Bereich 32..126plus eine neue Zeile beschränkt sind.
  • Sie können davon ausgehen, dass die Eingabe nicht länger als 10.000 Zeichen ist (im Idealfall sollte die Eingabe theoretisch unbegrenzt sein).
  • Ihr Code sollte ziemlich schnell fertig sein. Das obige Beispiel sollte im schlimmsten Fall nicht länger als eine Minute dauern. (Dies soll brachiale Gewalt ausschließen.)
  • Die Bewertung erfolgt in Byte.

Beispiele

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

Viel Spaß beim Codieren!


Beachten Sie, dass diese ähnliche Frage eng verwandt ist, auch wenn es sich um ein Duplikat handelt. Doch der Konsens bisher ist auf Meta , dass die ältere sollte ein Duplikat dieser eine in Betracht gezogen werden.


1
Ich bin mit Ihrer Bemerkung nicht einverstanden: Es ist dieselbe Frage, die für die vorhandenen Antworten eine einfache Umwandlung des Ausgabeformats erfordert, und außerdem ist jede Antwort auf diese Frage automatisch eine Antwort auf die vorherige Frage.
Peter Taylor

@PeterTaylor: Ich möchte noch einmal darum bitten, dass Sie diese Frage erneut öffnen. Die Spezifikation in dieser ist besser (wie von Martin gesagt) und ich möchte neuere, bessere Antworten sehen, einschließlich Pyth- und CJam-Antworten. Ich denke, es schadet nicht, beide Fragen offen zu lassen, weil sie ausreichend unterschiedlich sind. Nur zwei von fünf Benutzern, die diese Frage gestellt haben, waren kürzlich auf dieser Website.
El'endia Starman

@ PeterTaylor: Nach diesem Standard würde ich auch sagen, dass ich nicht glaube, dass Antworten zwischen Fragen kopiert werden können und wettbewerbsfähig bleiben. Schließlich ist die andere Frage vier Jahre alt . Es wäre gut, eine neue Version zu haben.
El'endia Starman

In Ihrem Beispiel von habe this question is about huffman codingich die Anzahl der Bits mit 145 und nicht mit 136 gezählt.
TFeld

1
Ich habe wirklich versucht, diese Herausforderung in Spoon abzuschließen , aber nach 2 Stunden Brainfuckery entschied ich, dass es am besten ist, aufzugeben ...
Bassdrop Cumberwubwubwub

Antworten:


2

Pyth, 53 Bytes

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

Demonstration

Hier ist eine Version, die den internen Status anzeigt, sodass Sie sehen können, wie die Codierung erstellt wird:

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

Demonstration

Kopieren Sie die Ausgabe in eine Umgebung mit breiteren Linien, um ein klareres Bild zu erhalten.


4

Python 2, 299 Bytes

Hier ist mein Versuch einer Antwort.

Die Huffman-Codes unterscheiden sich von den angegebenen Beispielen, sollten aber immer noch optimal sein.

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 Bytes

tabulatemacht eine Häufigkeitstabelle. huffmandictNimmt eine Liste von Symbolen und Wahrscheinlichkeiten für jedes Symbol und berechnet den Code.

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

Rubin, 189 180 Bytes

In Arbeit.

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

Es ist eine anonyme Funktion. Weisen Sie es zum Beispiel etwas zu fund rufen Sie es mit auf

f["some test string"]`

Das gibt einen Hash wie diesen zurück:

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

1

Haskell, 227 Bytes

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

Anwendungsbeispiel:

*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

Wie es funktioniert:

pAufrufe, fdie die Huffman-Tabelle in Form einer Liste von (Zeichen-, Codierungs-) Paaren erstellen, z. B. [ ('a',"0"), ('b',"1") ]die Tabelle nach der Länge der Codierungen sortieren, jedes Paar für die Ausgabe formatieren und mit dazwischen liegenden Zeilenumbrüchen verbinden.

fprüft zunächst die Groß- und Kleinschreibung und gibt die entsprechende Tabelle zurück. Sonst ist es die Eingabezeichenfolge und Gruppen - Sequenzen von gleichen Zeichen sortiert (zB "ababa"-> ["aaa","bb"]) und ordnet sie paarweise (sequence , [(char, "")]), (-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]. Das erste Element wird verwendet, Spur der Frequenz zu halten, ist das zweite Element eine Liste von Paaren eines Zeichen und es wird kodiert , (das anfänglich leer ist ). Diese sind alle Einzelelement - Huffman - Tabellen , wie erwartet durch pund werden kombiniert durch gund h.

gsortiert die Liste der Paare nach der Länge des ersten Elements, dh nach der Häufigkeit und den Anrufen h. hkombiniert die Huffman-Tabellen der ersten beiden Elemente, indem die Frequenzen verkettet werden und ein 0( 1) vor jedes Element der ersten (zweiten) Tabelle gesetzt wird. Verketten Sie beide Tabellen. Rufen Sie gerneut an, hören Sie auf, wenn ein einzelnes Element übrig ist, werfen Sie den Frequenzteil weg und geben Sie die vollständige Huffman-Tabelle zurück.


1

K (ngn / k) , 78 Bytes

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

Probieren Sie es online!

Gibt eine Liste der zu druckenden Zeichenfolgen zurück

h::0#'xErstellt eine leere Liste für jedes Zeichen (technisch gesehen wird jedes Zeichen in Länge 0 geändert). Wir werden die umgekehrten Huffman-Codes dort speichern. Wir verwenden ::statt :für die Zuweisung, um hglobal zu machen, damit es in Unterfunktionen sichtbar ist.

.=x ist eine Liste von Listen - die Indizes der Zeichenfolge, gruppiert nach Zeichenwert

(#1_) ist eine Funktion, die true zurückgibt, wenn die Länge des Arguments> 1 ist (technisch gesehen "Länge von 1 Tropfen ...")

(#1_){... }/bedeutet: Während das Argument eine Länge> 1 hat, wenden Sie weiterhin die geschweifte Klammer an

x@<#'x Sortieren Sie das Argument nach Länge

0 2_ Schneiden Sie es in einen 2-Element-Kopf und einen Schwanz

{h[x],:!2;y,,,/x}Aktualisierung hdurch Anhängen von 0 und 1 an die im Kopf enthaltenen Indizes; Geben Sie den Schwanz mit dem Kopf als einzelnes Element zurück

(?,/'x,'" ",'|'$h)(?x)?>#'=xkehren Sie jedes hZeichen um, sortieren Sie es, stellen Sie es eindeutig dar, stellen Sie entsprechende Zeichen voran und formatieren Sie es gut


0

JavaScript (ES6) 279

Im Wesentlichen der grundlegende Algorithmus von Wikipedia. Ich kann es wahrscheinlich besser machen.

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

Weiter unten im Snippet besser lesbar

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>

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.