: Huffman coding1952使[1][2]

使

JPEGZIP (Deflate) 使


種類

編集



1調1 (Static Huffman coding) 

deflatedeflate: Dynamic Huffman coding

1 (Adaptive Huffman coding) 

符号化の原理

編集

 DAEBCBACBBBC 12

5使13


  A   B   C   D   E
 000 001 010 011 100

使
  D   A   E   B   C   B   A   C   B   B   B   C
 011 000 100 001 010 001 000 010 001 001 001 010

12 x 336


   A   B   C   D    E
  110  0  10  1110 1111

使
   D   A   E   B  C B  A   C B B B  C
 1110 110 1111 0 10 0 110 10 0 0 0 10

25  70% 

ハフマン符号の構成

編集





(一)

(二)2

(三)

0101
 
ハフマン木

入力 DAEBCBACBBBC に対して上記のアルゴリズムを適用すると、

出現頻度と割り当てられた符号

文字 個数 符号
B 5 0
C 3 10
A 2 110
D 1 1110
E 1 1111

が得られ、個数の多い文字ほど短い符号が割り当てられていることがわかる。

接頭符号

編集

ハフマン符号は、接頭符号である。つまり、ある文字に対応するビット列が、他の文字に対応するビット列の接頭辞になることはない。この特徴のおかげで、デコーダはビット列を先頭から一度読むだけで元のメッセージを一意にデコードできる。

利用例

編集

算術符号やその他の高効率の符号化法と異なり、特許の問題が無い。 そのため、JPEGZIPなどの圧縮フォーマットで使用されている。

実装

編集

Groovyで実装を説明する。まず、符号情報とハフマン木のクラスを作る。

class CodeInfo { int code, codeSize }

class TreeNode implements Comparable<TreeNode> {
  byte value; int occurrence; List<TreeNode> children;
  int compareTo(TreeNode that) { this.occurrence - that.occurrence }
}

すると、バイト値→符号情報のテーブルは以下のようにして作れる。

CodeInfo[] createValue2Code(byte[] data) {
  // 各バイト値の発生回数を数える
  TreeNode[] nodes = new TreeNode[256]
  for (int i in 0..<256) { nodes[i] = new TreeNode(value: i) }
  for (byte v in data) { nodes[v].occurrence++ }

  // ハフマン木を作成
  PriorityQueue<TreeNode> queue = new PriorityQueue<TreeNode>(nodes as List)
  while (queue.size() > 1) {
    TreeNode n1 = queue.poll(), n2 = queue.poll()
    queue << new TreeNode(occurrence: n1.occurrence + n2.occurrence, children: [n1, n2])
  }
  TreeNode root = queue.poll()

  // 深さ優先探索でバイト値→符号情報を作成
  CodeInfo[] value2code = new CodeInfo[256]
  dfs(value2code, root, 0, 0)
  return value2code
}

void dfs(CodeInfo[] value2code, TreeNode node, int code, int codeSize) {
  if (node.children == null) {
    value2code[node.value] = new CodeInfo(code: code, codeSize: codeSize)
  } else {
    dfs(value2code, node.children[0], code << 1, codeSize + 1)
    dfs(value2code, node.children[1], (code << 1) + 1, codeSize + 1)
  }
}

圧縮のために以下のようなビットストリームのクラスを作る。

class BitStream {
  BitSet bs = new BitSet(); int len = 0, pos = 0;
  void write(int v, int bits) {
    for (int i in 0..<bits) { bs[len++] = ((v >>> i) & 1) != 0 }
  }
  int read(int bits) {
    int v = 0; for (int i in 0..<bits) { if (bs[pos++]) { v |= 1 << i } }
    return v
  }
  String toString() { "length = $len, {" + (0..<len).findAll({ bs[it] }).join(", ") + "}" }
}

すると、バイト値→符号情報のテーブルを使い以下のようにして圧縮できる。

void compress(BitStream bs, byte[] data, CodeInfo[] value2code) {
  for (byte v in data) {
    CodeInfo codeInfo = value2code[v]
    bs.write(codeInfo.code, codeInfo.codeSize)
  }
}

脚注

編集
  1. ^ David A. Huffman, "A method for the construction of minimum-redundancy codes" (PDF), Proceedings of the I.R.E., Sept. 1952, pp. 1098-1102 (ハフマンのオリジナル論文)
  2. ^ 『誰が音楽をタダにした?』早川書房、2016年、22頁。 

関連項目

編集