SlideShare uma empresa Scribd logo
1 de 29
ハフマン符号長の効率的な求め方
モチベーション ,[object Object],[object Object],[object Object],[object Object]
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 シンボルの数  n  に対してサイズ  2n  の配列 A を用意する。この配列で左上の表のハフマン木を表現することを考える 例 :  シンボル  d  のシンボル番号  ( 文字コード相当 )  は  3  で出現回数が  13  回
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 10 11 2 13 22 23 5 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 配列 A の右半分を出現回数で埋める。つまり、右半分の要素それぞれがハフマン木の葉に相当する。 シンボル番号を  i 、 i  の出現回数を  c i とすると  A[n + i] = c i 例えば、左上表からシンボル  b  は番号  1 、 c 1  = 11  なので  A[n + 1] = 11 (n = 8)
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 8 9 10 11 12 13 14 15 10 11 2 13 22 23 5 13 左半分を、各シンボルの葉へのポインタにする。具体的には、左側の要素の添え字がシンボル番号、値がポイントすべき右側の要素の添え字になる 例えば左上表からシンボル番号  i = 0  の  'a'  を考える。 A[0]  は右半分の要素の中で  'a'  の出現頻度に相当している要素を指す。よって  A[0] = 8  になる。 A[A[0]] = 10  とすれば  'a'  の出現頻度  10  が得られる 一般化すると  A[A[i]] = C i  になるように配列の左半分を埋めるということ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 10 9 14 11 12 13 8 15 10 11 2 13 22 23 5 13 次に出現頻度の値を比較キーにして二分木ヒープ (※) を構成する。このとき右半分の出現頻度の配列、つまり葉で直接ヒープを構成するのではなく、左半分のポインタで間接的にヒープを構成する。右半分の値  ( 葉 )  は変更せず、左半分だけが必要に応じて入れ替わる。 これにより  A[0]  がヒープの根になる。つまり、 A[0]  は出現頻度最小の値をポイントすることになる ※ 配列で二分木ヒープを構成する場合、親の添え字を  i  とすると左の子は  2i + 1,  右の子は  2i + 2 。「親の指す値が常に子のそれよりも小さい」という制約を満たすのがヒープの条件 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 いよいよハフマン木の構築を始める。通常のハフマン木構築アルゴリズム同様、出現頻度が最も小さい二つの値を (greedy  に ) 見つけたい。ひとつは、ヒープの根である  A[0]  がポイントする最小値である。 10 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 一時変数  m1  に  A[0]  を取り出し、 10 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 10 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 左半分の配列の末尾の値を先頭にコピー ( 上書き ) し、 15 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 [0, n -1]  だった左半分の配列の範囲を一つ減らして  [0, n - 2]  の範囲でヒープを構成しなおす。 これで最初の最小値である  2 ( を指しているポインタ )  をヒープから取り出して、ヒープを再構成したことになる。 14 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 次の最小値は構成しなおしたヒープの根  A[0]  にある。よって次の最小値は  A[A[0]] = A[14] = 5 14 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 二つ目の最小値を一次変数  m2  に取り出す。 ( 今回はヒープの再構成はまだ走らせないことに注意 ) 14 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 5 13 ここまでで最小の出現頻度の値二つが分かった。通常のハフマンアルゴリズムではここでこの二値の和を取ってできた節をハフマン木の新しい内部節  =  親とするわけだが、それと同等のことを行う。 ヒープから要素を一つ取り出して空いた領域  A[7]  があるので、そこに二値の和  A[m1] + A[m2] = 7  の値を入れる 14 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 5 13 A[0]  の値は既に  m2  にコピーしてあり要らないので、そこから新しい要素をポイントする。 7 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 最小値として扱った要素  A[m1]  と  A[m2]  も用済み。 A[m1] 、 A[m2]  からも新しい要素 ( つまり親 )  をポイントする。すなわち  A[m1] = A[m2] =  A[0] これで葉  ( もしくは内部節 ) から親を参照したことになる 7 7 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 新しく追加した要素を加味して、左側のヒープを構成しなおす。 ( この場合、新しく追加した要素  A[7] = 7  は最小なのでヒープに変更はなし ) ここまでで、ハフマン木構築処理のうち、最小の二つの要素を取って和を取り新しい要素にし、ヒープを次の繰り返しに備えて再構築する、という手順が一回完了した。 7 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 以下、ここまでの手順を繰り返す 根で最小値を指す  A[0]  を取り出し、 7 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 15 7 左半分の配列の末尾の値を先頭にコピー ( 上書き ) し、 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 15 11 12 13 15 7 10 11 13 22 23 7 13 8 7 [0, n -2]  だった左半分の配列の範囲を一つ減らして  [0, n - 3]  の範囲でヒープを構成しなおす 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 15 11 12 13 15 7 10 11 13 22 23 7 13 8 7 二つ目の最小値を指すポインタを  m2  に取り出し、 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 15 11 12 13 17 7 10 11 13 22 23 7 13 8 7 A[m1] + A[m2] = 17  を、ヒープから要素を取り出したことで空いていた要素に入れる 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 9 15 11 12 13 17 6 6 11 13 22 23 7 13 6 7 用済みの要素である先頭、今回扱った二つの最小値要素から、今できた新しい要素をポイントする 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 11 15 6 12 13 17 6 6 11 13 22 23 7 13 9 7 ヒープを構成しなおす。 これでハフマン木構築の繰り返し 2 回目が完了。このまま繰り返していくと、ループのたびにヒープ ( 青の要素 ) とオリジナルの出現頻度値を保持していた要素 ( 橙色の要素 ) が少なくなっていき、親をポイントしている要素 ( 緑の要素 ) が増えていくことが直感的にわかる 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 99 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.  a 10  4. e 22 1.  b 11  5. f 23 2.  c 2  6. g 5 3.  d 13  7. h 13 99 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 ,[object Object],[object Object],[object Object],[object Object],0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
99 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 ここで 子 -> 親はかならず右から左へ向かっている。ということは、逆に左から右、つまり先頭から末尾に向かうのは親から子に向かう動きになる。 この特徴を利用すると、左から順番に  A[i] = A[A[i]] + 1  していくだけで、 A[n + i]  にシンボル  i  の符号長が入る。これを理解するために動きを追ってみる。 まず根は  A[1] = 0  とする 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 次の要素  A[2]  は  1  で、この  1  は根である  A[1]  の添え字を指さしている。 さて、添え字を  1 -> 2  と移動してきたのは、左から右へ移動をしたということで、これはハフマン木の親から子を辿っていることに等しい。 ハフマン木では幹を一回辿るたびに符号長が  +1  されるわけだから、 ( 親が今辿った道までの符号長を記録していると考えると ) A[2]  までの符号長は親である  A[1]  の符号長  +1  である。 先に  A[1] = 0  としたのは、根は当然まだ幹を一度も辿ってないので、そこまでの符号長が  0  ということ。よって  A[2] = A[A[1]] + 1 = 0 + 1 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 同じように、 A[3] = 1 -> A[3] = A[A[1]] + 1 = 0 + 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 A[4] = 2  ということは、 A[4]  へは親である  A[2]  から辿ってくるということ。従って  A[4]  での符号長は  A[4] = A[A[2]] + 1 このように、左から順番に  A[i] = A[A[i]] + 1  するということは、親からひとつ子を辿ったら、親が持っていた符号の長さに  +1  したことに等しい 2n  まで処理すると最終的に以下のようになる 0 1 1 2 2 3 4 4 3 3 2 2 5 3 1 5 先にも述べたように、シンボル  i  の葉に相当するのは  A[n + i] 。赤枠で囲った右半分の配列の中身が、各シンボルの符号長である。例えば、シンボル  c ( 番号 2)  の符号長は  A[n + i] = A[8 + 2] = 5 おしまい 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
参考文献 ,[object Object]

Mais conteúdo relacionado

Destaque

Web-Gakkai Symposium 2010
Web-Gakkai Symposium 2010Web-Gakkai Symposium 2010
Web-Gakkai Symposium 2010Naoya Ito
 
Dijkstra Algorithm
Dijkstra AlgorithmDijkstra Algorithm
Dijkstra AlgorithmNaoya Ito
 
Introduction To Moco
Introduction To MocoIntroduction To Moco
Introduction To MocoNaoya Ito
 
Titanium Mobile
Titanium MobileTitanium Mobile
Titanium MobileNaoya Ito
 
Introduction to Algorithms#24 Shortest-Paths Problem
Introduction to Algorithms#24 Shortest-Paths ProblemIntroduction to Algorithms#24 Shortest-Paths Problem
Introduction to Algorithms#24 Shortest-Paths ProblemNaoya Ito
 
081108huge_data.ppt
081108huge_data.ppt081108huge_data.ppt
081108huge_data.pptNaoya Ito
 
about Thrift
about Thriftabout Thrift
about ThriftNaoya Ito
 
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...Naoya Ito
 
スペルミス修正プログラムを作ろう
スペルミス修正プログラムを作ろうスペルミス修正プログラムを作ろう
スペルミス修正プログラムを作ろうNaoya Ito
 
はてなブックマークのシステムについて
はてなブックマークのシステムについてはてなブックマークのシステムについて
はてなブックマークのシステムについてNaoya Ito
 
Perlで圧縮
Perlで圧縮Perlで圧縮
Perlで圧縮Naoya Ito
 
Sphinx で手軽に作るドキュメント
Sphinx で手軽に作るドキュメントSphinx で手軽に作るドキュメント
Sphinx で手軽に作るドキュメントGo Yamada
 
APIドキュメントの話 #sphinxjp
APIドキュメントの話 #sphinxjpAPIドキュメントの話 #sphinxjp
APIドキュメントの話 #sphinxjpTakeshi Komiya
 
Sphinxで社内勉強会(Git)の
資料を作ってみた
Sphinxで社内勉強会(Git)の
資料を作ってみたSphinxで社内勉強会(Git)の
資料を作ってみた
Sphinxで社内勉強会(Git)の
資料を作ってみたTaku SHIMIZU
 
ドキュメントシステムはこれを使え2015年版
ドキュメントシステムはこれを使え2015年版ドキュメントシステムはこれを使え2015年版
ドキュメントシステムはこれを使え2015年版Keiichiro Shikano
 
How to read linux kernel
How to read linux kernelHow to read linux kernel
How to read linux kernelNaoya Ito
 
オープンソースの情報共有の仕組み"Knowledge"のご紹介
オープンソースの情報共有の仕組み"Knowledge"のご紹介オープンソースの情報共有の仕組み"Knowledge"のご紹介
オープンソースの情報共有の仕組み"Knowledge"のご紹介koda3
 

Destaque (17)

Web-Gakkai Symposium 2010
Web-Gakkai Symposium 2010Web-Gakkai Symposium 2010
Web-Gakkai Symposium 2010
 
Dijkstra Algorithm
Dijkstra AlgorithmDijkstra Algorithm
Dijkstra Algorithm
 
Introduction To Moco
Introduction To MocoIntroduction To Moco
Introduction To Moco
 
Titanium Mobile
Titanium MobileTitanium Mobile
Titanium Mobile
 
Introduction to Algorithms#24 Shortest-Paths Problem
Introduction to Algorithms#24 Shortest-Paths ProblemIntroduction to Algorithms#24 Shortest-Paths Problem
Introduction to Algorithms#24 Shortest-Paths Problem
 
081108huge_data.ppt
081108huge_data.ppt081108huge_data.ppt
081108huge_data.ppt
 
about Thrift
about Thriftabout Thrift
about Thrift
 
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...
SmartPhone development guide with CoffeeScript + Node + HTML5 Technology, for...
 
スペルミス修正プログラムを作ろう
スペルミス修正プログラムを作ろうスペルミス修正プログラムを作ろう
スペルミス修正プログラムを作ろう
 
はてなブックマークのシステムについて
はてなブックマークのシステムについてはてなブックマークのシステムについて
はてなブックマークのシステムについて
 
Perlで圧縮
Perlで圧縮Perlで圧縮
Perlで圧縮
 
Sphinx で手軽に作るドキュメント
Sphinx で手軽に作るドキュメントSphinx で手軽に作るドキュメント
Sphinx で手軽に作るドキュメント
 
APIドキュメントの話 #sphinxjp
APIドキュメントの話 #sphinxjpAPIドキュメントの話 #sphinxjp
APIドキュメントの話 #sphinxjp
 
Sphinxで社内勉強会(Git)の
資料を作ってみた
Sphinxで社内勉強会(Git)の
資料を作ってみたSphinxで社内勉強会(Git)の
資料を作ってみた
Sphinxで社内勉強会(Git)の
資料を作ってみた
 
ドキュメントシステムはこれを使え2015年版
ドキュメントシステムはこれを使え2015年版ドキュメントシステムはこれを使え2015年版
ドキュメントシステムはこれを使え2015年版
 
How to read linux kernel
How to read linux kernelHow to read linux kernel
How to read linux kernel
 
オープンソースの情報共有の仕組み"Knowledge"のご紹介
オープンソースの情報共有の仕組み"Knowledge"のご紹介オープンソースの情報共有の仕組み"Knowledge"のご紹介
オープンソースの情報共有の仕組み"Knowledge"のご紹介
 

Último

Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdffurutsuka
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 

Último (9)

Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdf
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 

090518computing Huffman Code Length

  • 2.
  • 3. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 シンボルの数 n に対してサイズ 2n の配列 A を用意する。この配列で左上の表のハフマン木を表現することを考える 例 : シンボル d のシンボル番号 ( 文字コード相当 ) は 3 で出現回数が 13 回
  • 4. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 10 11 2 13 22 23 5 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 配列 A の右半分を出現回数で埋める。つまり、右半分の要素それぞれがハフマン木の葉に相当する。 シンボル番号を i 、 i の出現回数を c i とすると A[n + i] = c i 例えば、左上表からシンボル b は番号 1 、 c 1 = 11 なので A[n + 1] = 11 (n = 8)
  • 5. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 8 9 10 11 12 13 14 15 10 11 2 13 22 23 5 13 左半分を、各シンボルの葉へのポインタにする。具体的には、左側の要素の添え字がシンボル番号、値がポイントすべき右側の要素の添え字になる 例えば左上表からシンボル番号 i = 0 の 'a' を考える。 A[0] は右半分の要素の中で 'a' の出現頻度に相当している要素を指す。よって A[0] = 8 になる。 A[A[0]] = 10 とすれば 'a' の出現頻度 10 が得られる 一般化すると A[A[i]] = C i になるように配列の左半分を埋めるということ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 6. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 10 9 14 11 12 13 8 15 10 11 2 13 22 23 5 13 次に出現頻度の値を比較キーにして二分木ヒープ (※) を構成する。このとき右半分の出現頻度の配列、つまり葉で直接ヒープを構成するのではなく、左半分のポインタで間接的にヒープを構成する。右半分の値 ( 葉 ) は変更せず、左半分だけが必要に応じて入れ替わる。 これにより A[0] がヒープの根になる。つまり、 A[0] は出現頻度最小の値をポイントすることになる ※ 配列で二分木ヒープを構成する場合、親の添え字を i とすると左の子は 2i + 1, 右の子は 2i + 2 。「親の指す値が常に子のそれよりも小さい」という制約を満たすのがヒープの条件 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 7. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 いよいよハフマン木の構築を始める。通常のハフマン木構築アルゴリズム同様、出現頻度が最も小さい二つの値を (greedy に ) 見つけたい。ひとつは、ヒープの根である A[0] がポイントする最小値である。 10 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 8. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 一時変数 m1 に A[0] を取り出し、 10 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 10 m1
  • 9. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 14 11 12 13 8 15 10 11 13 22 23 5 13 左半分の配列の末尾の値を先頭にコピー ( 上書き ) し、 15 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 10. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 [0, n -1] だった左半分の配列の範囲を一つ減らして [0, n - 2] の範囲でヒープを構成しなおす。 これで最初の最小値である 2 ( を指しているポインタ ) をヒープから取り出して、ヒープを再構成したことになる。 14 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 11. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 次の最小値は構成しなおしたヒープの根 A[0] にある。よって次の最小値は A[A[0]] = A[14] = 5 14 2 10 m1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 12. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 15 10 11 13 22 23 5 13 二つ目の最小値を一次変数 m2 に取り出す。 ( 今回はヒープの再構成はまだ走らせないことに注意 ) 14 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 13. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 5 13 ここまでで最小の出現頻度の値二つが分かった。通常のハフマンアルゴリズムではここでこの二値の和を取ってできた節をハフマン木の新しい内部節 = 親とするわけだが、それと同等のことを行う。 ヒープから要素を一つ取り出して空いた領域 A[7] があるので、そこに二値の和 A[m1] + A[m2] = 7 の値を入れる 14 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 14. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 5 13 A[0] の値は既に m2 にコピーしてあり要らないので、そこから新しい要素をポイントする。 7 2 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 15. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 最小値として扱った要素 A[m1] と A[m2] も用済み。 A[m1] 、 A[m2] からも新しい要素 ( つまり親 ) をポイントする。すなわち A[m1] = A[m2] = A[0] これで葉 ( もしくは内部節 ) から親を参照したことになる 7 7 10 m1 14 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 16. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 新しく追加した要素を加味して、左側のヒープを構成しなおす。 ( この場合、新しく追加した要素 A[7] = 7 は最小なのでヒープに変更はなし ) ここまでで、ハフマン木構築処理のうち、最小の二つの要素を取って和を取り新しい要素にし、ヒープを次の繰り返しに備えて再構築する、という手順が一回完了した。 7 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 17. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 以下、ここまでの手順を繰り返す 根で最小値を指す A[0] を取り出し、 7 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 18. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 8 11 12 13 15 7 10 11 13 22 23 7 13 15 7 左半分の配列の末尾の値を先頭にコピー ( 上書き ) し、 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 19. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 15 11 12 13 15 7 10 11 13 22 23 7 13 8 7 [0, n -2] だった左半分の配列の範囲を一つ減らして [0, n - 3] の範囲でヒープを構成しなおす 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 20. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 15 11 12 13 15 7 10 11 13 22 23 7 13 8 7 二つ目の最小値を指すポインタを m2 に取り出し、 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 21. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 15 11 12 13 17 7 10 11 13 22 23 7 13 8 7 A[m1] + A[m2] = 17 を、ヒープから要素を取り出したことで空いていた要素に入れる 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 22. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 9 15 11 12 13 17 6 6 11 13 22 23 7 13 6 7 用済みの要素である先頭、今回扱った二つの最小値要素から、今できた新しい要素をポイントする 8 m2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 7 m1
  • 23. 0. a 10 4. e 22 1. b 11 5. f 23 2. c 2 6. g 5 3. d 13 7. h 13 11 15 6 12 13 17 6 6 11 13 22 23 7 13 9 7 ヒープを構成しなおす。 これでハフマン木構築の繰り返し 2 回目が完了。このまま繰り返していくと、ループのたびにヒープ ( 青の要素 ) とオリジナルの出現頻度値を保持していた要素 ( 橙色の要素 ) が少なくなっていき、親をポイントしている要素 ( 緑の要素 ) が増えていくことが直感的にわかる 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 24.
  • 25.
  • 26. 99 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 ここで 子 -> 親はかならず右から左へ向かっている。ということは、逆に左から右、つまり先頭から末尾に向かうのは親から子に向かう動きになる。 この特徴を利用すると、左から順番に A[i] = A[A[i]] + 1 していくだけで、 A[n + i] にシンボル i の符号長が入る。これを理解するために動きを追ってみる。 まず根は A[1] = 0 とする 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 27. 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 次の要素 A[2] は 1 で、この 1 は根である A[1] の添え字を指さしている。 さて、添え字を 1 -> 2 と移動してきたのは、左から右へ移動をしたということで、これはハフマン木の親から子を辿っていることに等しい。 ハフマン木では幹を一回辿るたびに符号長が +1 されるわけだから、 ( 親が今辿った道までの符号長を記録していると考えると ) A[2] までの符号長は親である A[1] の符号長 +1 である。 先に A[1] = 0 としたのは、根は当然まだ幹を一度も辿ってないので、そこまでの符号長が 0 ということ。よって A[2] = A[A[1]] + 1 = 0 + 1 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 同じように、 A[3] = 1 -> A[3] = A[A[1]] + 1 = 0 + 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 28. 0 1 1 2 2 4 6 6 5 5 3 3 7 4 1 7 A[4] = 2 ということは、 A[4] へは親である A[2] から辿ってくるということ。従って A[4] での符号長は A[4] = A[A[2]] + 1 このように、左から順番に A[i] = A[A[i]] + 1 するということは、親からひとつ子を辿ったら、親が持っていた符号の長さに +1 したことに等しい 2n まで処理すると最終的に以下のようになる 0 1 1 2 2 3 4 4 3 3 2 2 5 3 1 5 先にも述べたように、シンボル i の葉に相当するのは A[n + i] 。赤枠で囲った右半分の配列の中身が、各シンボルの符号長である。例えば、シンボル c ( 番号 2) の符号長は A[n + i] = A[8 + 2] = 5 おしまい 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • 29.