ハフマンをコーディングしてください!


13

さもなければ、彼はあなたの家を吹き飛ばして吹き飛ばします!

それは完全に無関係でした。この挑戦は実際にハフマンコーディングについてです。その要点は、特定のテキスト内の文字の頻度を利用して、その表現を短くすることです。言い換えれば、私たちのアルファベットがa通り抜けているzとしましょう。27文字です。5ビットには32文字分のスペースがあるため、それぞれを5ビットで一意にエンコードできます。ただし、多くの状況(英語や一般的な言語など)では、一部の文字は他の文字よりも頻繁に出現します。頻度の高い文字にはより少ないビットを使用し、頻度の低い文字には(おそらく)より多くのビットを使用できます。正しく実行すると、ビット数が全体的に節約され、元のテキストを一意に再構築できます。

「この質問はハフマンコーディングに関するものです」を例に取りましょう。このテキストの長さは37文字で、通常は37 * 8 = 296ビットですが、各文字に5ビットしか使用しない場合は37 * 5 = 185ビットのみです。心に留めておきます。

以下に、各文字とテキスト内のそれらの頻度の(ソート)テーブルを、頻度の高い順に並べています(_はスペースを表します)。

_ 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ビットを使用するよりも優れたエンコーディングになることはすぐに明らかになるはずです。しかし、どれだけ良いかを調べましょう!

185 ビットと比較して145ビット!これは40ビットの節約、または20%を超える節約です!(もちろん、これは構造に関する情報がデコードに利用可能であることを前提としています。)このコーディングは、文字の表現を変更することでビットを落とすことができないため最適です。

タスク

  • 1つのパラメーターを持つプログラムまたは関数を記述します...
  • STDIN(または同等のもの)から、または単一の引数として入力を受け取ります。
  • 周波数でソートされた文字を使用して、上記の最適なハフマンコーディングを出力します(周波数クラス内の順序は関係ありません)。
  • 入力の文字は、ASCII範囲32..126と改行に制限されていると想定できます。
  • 入力が10,000文字以下であると仮定することができます(理論的には、入力は無制限である必要があります)。
  • コードはかなり速く終了するはずです。上記の例では、最悪でも1分程度しかかかりません。(これは、ブルートフォースを排除することを目的としています。)
  • スコアリングはバイト単位です。

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

ハッピーコーディング!


この類似の質問は、この質問が重複しているという点であっても密接に関連していることに注意してください。しかし、メタに関するこれまでコンセンサスは、古いものはこのものの複製と見なされるべきであるということです。


1
私はあなたのノートに同意しません:それは既存の答えのために出力フォーマットの簡単な変換を必要とするのと同じ質問であり、さらにこの質問に対する答えは自動的に前の質問に対する答えです。
ピーターテイラー

@PeterTaylor:この質問を再開するようにもう一度申し上げたいです。この仕様はより優れており(Martinによると)、PythやCJamの回答を含む、より新しく、より良い回答を期待しています。両方の質問が十分に異なっているので、両方の質問を開いたままにしておいても問題はないと思います。その質問に投稿した5人のユーザーのうち、最近このサイトにアクセスしたのは2人だけです。
エレンディアスターマン

@PeterTaylor:また、この基準に沿って、質問間で回答をコピーして競争力を維持できるとは思いません。最後に、もう1つの質問は4歳です。新しいバージョンを用意しておくといいでしょう。
エレンディアスターマン

の例ではthis question is about huffman coding、ビット数を136ではなく145とカウントしました。
TFeld15年

1
私は実際にこの課題を完成しようとしていたスプーン ...が、brainfuckeryの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バイト

これが私の答えです。

ハフマンコードは、与えられた例とは異なりますが、それでも最適なはずです。

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

使い方:

pf(文字、エンコード)ペアのリストの形式でハフマンテーブルを作成する呼び出し。[ ('a',"0"), ('b',"1") ]、エンコードの長さでテーブルをソートし、各ペアを出力用にフォーマットし、間に改行を結合します。

f最初に1文字の大文字と小文字をチェックし、対応するテーブルを返します。それ以外の場合は、入力文字列をソートし、等しい文字のシーケンスをグループ化し(たとえば、"ababa"-> ["aaa","bb"])、ペア(sequence , [(char, "")])にマッピングします(-> [ ("aaa", [('a',"")]), ("bb", [('b', "")])]。最初の要素は頻度を追跡するために使用され、2番目の要素は文字のペアのリストです(最初は空である)をコードだ。によって予想されるように、これらは、すべて単一の要素ハフマンテーブルであるpとすることによって合成されるgh

gペアのリストを最初の要素の長さ、つまり頻度と呼び出しでソートしますhh頻度を連結し、最初の(2番目の)テーブルのすべての要素の前に01)を置くことにより、最初の2つの要素のハフマンテーブルを結合します。両方のテーブルを連結します。gもう一度呼び出して、1つの要素が残ったら停止し、周波数部分を捨てて、完全なハフマンテーブルを返します。


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に変更します)。反転したハフマンコードをそこに保存します。代入の::代わりに使用:して、hグローバルにし、サブ関数で見えるようにします。

.=x リストのリストです-文字値でグループ化された文字列のインデックス

(#1_) 引数の長さが> 1(技術的には「1ドロップの長さ...」)である場合、真理値を返す関数です。

(#1_){... }/意味:引数の長さが1より大きい間、中括弧関数を適用し続けます

x@<#'x 引数を長さでソートする

0 2_ 2要素の頭と尾に切ります

{h[x],:!2;y,,,/x}hヘッドに含まれるインデックスに0と1を追加して更新します。単一の要素として頭部を持つ尾部を返す

(?,/'x,'" ",'|'$h)(?x)?>#'=xのそれぞれを反転しh、ソートし、一意にし、対応する文字を追加し、適切にフォーマットします


0

JavaScript(ES6)279

基本的に、ウィキペディアの基本的なアルゴリズム。私はおそらくもっとうまくできるでしょう。

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.