Java プログラミング スキル - データ構造とアルゴリズム「ハフマン コーディング」

Java プログラミング スキル - データ構造とアルゴリズム「ハフマン コーディング」

基本的な紹介

  • ハフマン符号化は、(ハフマンコーディング) とも訳されます。ハフマン符号化は、ハフマンコーディングとも呼ばれ、コーディング方法およびプログラムアルゴリズムです。
  • ハフマン符号化は、電気通信におけるハフマンツリーの典型的な応用シナリオの 1 つです。
  • ハフマン符号化はデータ ファイルの圧縮に広く使用されています。圧縮率は通常 20% から 90% の間です。
  • ハフマン コードは可変長符号化 (VLC) の一種です。ハフマンは 1952 年に、最適符号化と呼ばれる符号化方式を提案しました。

原理分析

通信分野における情報処理方式1:固定長符号化

たとえば、i like like like java do you like a javaはスペースを含めて合計40文字です。対応するASCIIコードとバイナリエンコードは次のとおりです。

バイナリデータの合計長は359(スペースを含む)です。

通信分野における情報処理方式2:可変長符号化

i like like like java do you like a java は、スペースを含めて合計 40 文字です。可変長エンコード処理は以下の通り

文字のエンコーディングは、他の文字エンコーディングのプレフィックスになることはできません。この要件を満たすエンコーディングはプレフィックス エンコーディングと呼ばれ、重複したエンコーディングは一致しないことを意味します。

通信分野における情報処理方法3:ハフマン符号化

i like like like java do you like a java は、スペースを含めて合計 40 文字です。可変長エンコード処理は以下の通り

上記の文字の出現回数を重みとして使用して、その回数に応じてハフマン ツリーを構築します。

ハフマン ツリーによれば、各文字にはコード (プレフィックス コード) が割り当てられ、左へのパスは 0、右へのパスは 1 です。コードは次のようになります。

上記のハフマン符号化によれば、「i like like like java do you like a java」という文字列に対応するエンコードは次のようになります (ここではロスレス圧縮を使用していることに注意してください)。

例:

元の長さは359で、(359-133)/359=62.9%に圧縮されます。

このエンコーディングはプレフィックス エンコーディングの要件を満たしています。つまり、文字のエンコーディングは他の文字エンコーディングのプレフィックスになることはできません。一致の曖昧さは発生しません。

ハフマン符号化はロスレス圧縮です!!

知らせ:

このハフマン ツリーはソート方法によって異なる場合があり、対応するハフマン コードはまったく同じではありませんが、wpl は同じで、最小になります。たとえば、新しく生成されたバイナリ ツリーを、同じ重みを持つバイナリ ツリーの中で常に最後にランク付けすると、生成されたバイナリ ツリーは次のようになります。

対応するハフマン木を作成する

データを圧縮するためのハフマン符号化の原理によれば、「私はJavaが好きですか、あなたはJavaが好きですか」に対応するハフマンツリーを作成する必要があります。

アイデア:

まず、Node ノードを作成します。Node {data{store data}, weight (weight), left, right};

「i like like like java do you like a java」に対応する byte[] 配列を取得します。

ハフマンツリーに構築するノードをリストに入れるメソッドを記述します。集める;

コレクションリストを使用することができます対応するハフマンツリーを作成します。

ハフマンツリーの応用例

文字列を圧縮および解凍する

  1. パッケージ com.xie.huffmancode;
  2.  
  3. java.util.* をインポートします。
  4.  
  5. パブリッククラスHuffmanCode {
  6. 公共 静的void main(String[] args) {
  7. 文字列 str = "私は Java が好きです、あなたは Java が好きですか" ;
  8. byte[] contentBytes = str.getBytes();
  9. システム.out .println( "contentBytes=" + Arrays.toString(contentBytes));
  10. リスト<Node> ノード = getNodes(contentBytes);
  11.  
  12. //ハフマン木を生成する
  13. ノード hufffmanTreeRoot = createHufffmanTree(nodes);
  14.  
  15. //生成されたハフマン符号化テーブル
  16. getCodes(hufffmanTreeRoot, "" , stringBuilder);
  17.  
  18. byte[] huffmanCodeBytes = zip(contentBytes, huffmanCodes);
  19. システム.out.println ( "huffmanCodeBytes = " + Arrays.toString(huffmanCodeBytes));
  20.  
  21. byte[] デコード = decode(huffmanCodes、huffmanCodeBytes);
  22. System.out.println ( "ハフマンデコード後の対応する配列" + new String(decode));
  23.  
  24. /**
  25. 107、101、32、108、105、107、101、32、108、105、107、101、32、106、97、118、97、32、100、111、32、121、111、117、32、108、105、107、101、32、97、32、106、97、118、97]
  26. * ハフマンコードバイト = [-88、-65、-56、-65、-56、-65、-55、77、-57、6、-24、-14、-117、-4、-60、-90、28]
  27. * ハフマンデコード後の対応する配列は次のようになります のように  Javaが好きですかJavaが好きですか?
  28. */
  29.  
  30. }
  31.  
  32. //データを解凍するというアイデアを完成させる
  33. //1. ハフマンコードバイト[-88、-65、-56、-65、-56、-65、-55、77、-57、6、-24、-14、-117、-4、-60、-90、28]
  34. // ハフマン コード"101010001011111111001000101111...."に対応するバイナリ文字列に変換します。  
  35. //2. ハフマン コーディングに対応するバイナリ文字列"101010001011111111001000101111...." => ハフマン コーディング テーブルを比較 => "i like like like java do you like a java"  
  36.  
  37. /**
  38. * 圧縮データのデコードを完了する
  39. *
  40. * @param huffmanCodes ハフマンコーディングテーブル
  41. * @param huffmanBytes ハフマンエンコードによって得られたバイト配列
  42. * @return元の文字列に対応する配列
  43. */
  44. 公共 静的byte[]デコード(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
  45. StringBuilder の stringBuilder = 新しい StringBuilder();
  46. ( int i = 0; i < huffmanBytes.length; i++) {
  47. // 最後のバイトかどうかをチェックする
  48. ブールフラグ = (i == huffmanBytes.length - 1);
  49. stringBuilder.append(byteToBitString(!flag, huffmanBytes[i]));
  50. }
  51.  
  52. Map<String, Byte> map = new HashMap<>();
  53. Map.Entry<Byte, String> エントリの場合: huffmanCodes.entrySet()) {
  54. バイト k = entry.getKey();
  55. 文字列 v = entry.getValue();
  56. map.put(v, k);
  57. }
  58.  
  59. リスト<Byte> リスト = 新しい ArrayList<>();
  60. ( int i = 0; i < stringBuilder.length();)の場合{
  61. 整数 カウント= 1;
  62. ブールフラグ = true ;
  63. バイト b = null ;
  64. while (フラグ) {
  65. String key = stringBuilder.substring ( i, i + count ); //i は変化せず、文字が一致するまでcountが移動します
  66. b = map.get(キー);
  67. b == null の場合
  68. カウント++;
  69. }それ以外{
  70. フラグ = false ;
  71. }
  72. }
  73. リストに追加(b);
  74. i +=カウント;
  75. }
  76. byte[] bytes = 新しいbyte[ list.size ()];
  77. ( int i = 0; i < list.size ( ) ); i++) {
  78. バイト[i] = list.get(i);
  79. }
  80. バイトを返します
  81. }
  82.  
  83. /**
  84. * バイトをバイナリ文字列に変換する
  85. *
  86. * @param フラグは、上位ビットを埋める必要があるかどうかを示します。 trueの場合、上位ビットを埋める必要があることを示します。 falseの場合、埋める必要がないことを意味します。最後のバイトの場合、上位ビットを埋める必要はありません。
  87. * @param b 受信バイト
  88. * @returnバイトに対応するバイナリ文字列(補数で返されることに注意してください)
  89. */
  90. 公共 静的文字列 byteToBitString(boolean flag, byte b) {
  91. //bをintに変換する 
  92. 整数 温度= b;
  93.  
  94. // tempが正の数の場合は、上位ビットを埋める必要があります
  95. if (フラグ) {
  96. // ビット単位の OR、例えば 256|1=> 1 0000 0000|0000 0001 => 1 0000 0001
  97. 温度|= 256;
  98. }
  99.  
  100. //返される値はtempの2進数の補数です
  101. 文字列 bitStr = Integer .toBinaryString( temp );
  102. if (フラグ) {
  103. // 最後の8ビットを取得します
  104. bitStr.substring ( bitStr.length () - 8)を返します
  105. }それ以外{
  106. bitStrを返します
  107. }
  108. }
  109.  
  110. /**
  111. * 元のバイト配列をハフマンバイト配列にカプセル化する
  112. *
  113. * @param バイト
  114. * @戻る 
  115. */
  116. 公共 静的byte[] huffmanZip(byte[] バイト) {
  117. リスト<Node> ノード = getNodes(bytes);
  118.  
  119. //ハフマン木を作成する
  120. ノード hufffmanTreeRoot = createHufffmanTree(nodes);
  121. //ハフマンコードを生成する
  122. getCodes(hufffmanTreeRoot, "" , stringBuilder);
  123. //圧縮されたハフマンエンコードされたバイト配列を返す
  124. zip(bytes, huffmanCodes)を返します
  125.  
  126. }
  127.  
  128. /**
  129. * 文字列に対応するbyte[]配列をハフマン符号化テーブルに渡し、ハフマン符号化された圧縮byte[]を返す
  130. *
  131. * @param bytes 元の文字列に対応するbyte[]
  132. * @param huffmanCodes 生成されたハフマンコード
  133. * @returnハフマン符号化後のbyte[]を返します
  134. */
  135. 公共 静的byte[] zip(byte[] バイト、Map<Byte、String> huffmanCodes) {
  136. //huffmanCodesを使用してバイトをハフマンコードに対応する文字列に変換します
  137. StringBuilder の stringBuilder = 新しい StringBuilder();
  138. (バイトb:バイト)の場合{
  139. 文字列ビルダーに追加します(huffmanCodes.get(b));
  140. }
  141.  
  142. // "10101000101111111001000101111...."を byte[] に変換します
  143. // 統計はbyte[] huffmanCodeBytesの長さを返す
  144. さ;
  145. (stringBuilder.length() % 8 == 0) の場合 {
  146. len = stringBuilder.length() / 8;
  147. }それ以外{
  148. len = stringBuilder.length() / 8 + 1;
  149. }
  150. //圧縮されたデータを格納するためのbyte[]配列を作成する
  151. byte[] huffmanCodeBytes = 新しいbyte[len];
  152. 整数 インデックス= 0;
  153. ( int i = 0 ; i < stringBuilder.length(); i += 8) {
  154. 文字列 strByte;
  155. if (i + 8 > stringBuilder.length()) {
  156. strByte = stringBuilder.substring (i);
  157. }それ以外{
  158. strByte = stringBuilder.substring (i, i + 8);
  159. }
  160. //strByte をバイトに変換し、huffmanCodeBytes に格納します
  161. huffmanCodeBytes[インデックス] = (バイト) Integer .parseInt(strByte, 2);
  162. インデックス++;
  163. }
  164. huffmanCodeBytesを返します
  165. }
  166.  
  167. //ハフマン木に対応するハフマン符号化テーブルを生成する
  168. // アイデア:
  169. //1. ハフマン符号化テーブルを 32->01,97->100... の形式で Map<Byte,String> に格納します。
  170. 静的Map<Byte, String> huffmanCodes = new HashMap<>();
  171. //2. ハフマンコーディングテーブルを生成するときは、パスを連結し、リーフノードのパスを格納するStringBuilderを定義する必要があります。
  172. 静的StringBuilder stringBuilder = 新しい StringBuilder();
  173.  
  174. /**
  175. * 渡されたノードのすべてのリーフのハフマンコードを取得し、それらをhuffmanCodesセットに格納します。
  176. *
  177. * @param node 受信ノード
  178. * @param コードパス: 左の子ノードは 0、右の子ノードは 1
  179. * @param stringBuilderはパスを連結するために使用されます
  180. */
  181. 公共 静的void getCodes(Node ノード、文字列コード、StringBuilder 文字列ビルダー) {
  182. StringBuilder の stringBuilder2 = 新しい StringBuilder(stringBuilder);
  183. stringBuilder2.append(コード);
  184. if (ノード ​​!= null ) {
  185. //現在のノードがリーフノードか非リーフノードかを判断する
  186. if (node.data == null ){//非リーフノード
  187.  
  188. //左への再帰処理
  189. getCodes( node.left , "0" , stringBuilder2);
  190. //右方向への再帰処理
  191. getCodes( node.right , "1" , stringBuilder2);
  192. } else {// リーフノード
  193. huffmanCodes.put(node.data、stringBuilder2.toString());
  194. }
  195. }
  196. }
  197.  
  198. // 事前順序トラバーサル
  199. 公共 静的void preOrder(ノードルート) {
  200. ルートがnull場合
  201. ルートを事前注文します。
  202. }それ以外{
  203. System.out.println ( "ハフマンツリーは空にできません~~" );
  204. }
  205. }
  206.  
  207. /**
  208. * バイト配列をノードコレクションに変換する
  209. *
  210. * @param bytes バイト配列
  211. * @戻る 
  212. */
  213. 公共 静的リスト<Node> getNodes(byte[] bytes) {
  214. ArrayList<Node> ノード = 新しい ArrayList<>();
  215.  
  216. //各バイトの出現回数を保存する
  217. Map<Byte, Integer > counts = new HashMap<>();
  218. (バイトb:バイト)の場合{
  219. counts.merge(b, 1, (a, b1) -> a + b1);
  220. }
  221.  
  222. //各キーと値のペアをノード オブジェクトに変換し、ノード コレクションに追加します。
  223. counts.forEach((k, v) -> nodes.add(新しい Node(k , v)));
  224. ノードを返します
  225. }
  226.  
  227. /**
  228. * ハフマン木を生成する
  229. * @param nodes 渡されたノード
  230. * @戻る 
  231. */
  232. 公共 静的ノードcreateHufffmanTree(List<Node>ノード) {
  233. ノードサイズ() > 1の場合{
  234. // 小さい順から大きい順に並べ替える
  235. コレクション.sort(ノード);
  236.  
  237. //(1) 重みが最小のノードを取り出す(二分木)
  238. ノード leftNode = nodes.get(0);
  239.  
  240. //(2) 2番目に小さい重みを持つノードを取り出す(二分木)
  241. ノード rightNode = nodes.get(1);
  242. //(3) 新しいバイナリツリーを構築する
  243. ノードの親 = 新しいノード ( null 、 leftNode.weight + rightNode.weight )。
  244. 親.left = leftNode ;
  245. 親.right = rightNode ;
  246.  
  247. //(4) ArrayListから処理済みのバイナリツリーを削除する
  248. ノードを削除します(左ノード)。
  249. ノードを削除します。(右ノード)
  250. //(5) ノードに親を追加する
  251. nodes.add (親);
  252. }
  253. //ノードの最後のノードはハフマン木のルートノードです
  254. nodes.get(0)を返します
  255. }
  256. }
  257.  
  258. //データと重みを持つノードを作成する
  259. クラスNodeはComparable<Node>を実装します。
  260. //データ自体を保存します。たとえば、 'a' => '97' ' ' => '32'  
  261. バイトデータ。
  262. //重み、文字の出現回数を示す
  263. 整数の重量;
  264.  
  265. ノード;
  266. ノード;
  267.  
  268. パブリックノード(バイトデータ、 int重み) {
  269. this.data = データ;
  270. this.weight = 重量;
  271. }
  272.  
  273. パブリックボイドpreOrder() {
  274. System.out.println (これ) ;
  275. if ( this.left != null ) {
  276. this.left.preOrder ();
  277. }
  278. if ( this.right != null ) {
  279. this.right .preOrder();
  280. }
  281. }
  282.  
  283. @オーバーライド
  284. 公共  int compareTo(ノードo) {
  285. // 小さい順から大きい順に並べ替える
  286. this.weight - o.weightを返します
  287. }
  288.  
  289. @オーバーライド
  290. パブリック文字列toString() {
  291. 戻る  「ノード{」 +
  292. 「データ=」 + データ +
  293. ", 重量=" + 重量 +
  294. '}' ;
  295. }
  296. }

ハフマン圧縮ファイルに関する注意事項

ファイル自体が圧縮されている場合、ビデオ、PPT、その他のファイルなどの圧縮にハフマン符号化を使用した後でも、効率は大幅に変化しません。

ハフマン符号化はバイトごとに処理されるため、すべてのファイル(バイナリファイル、テキストファイル)を処理できます。

ファイルの内容に重複データがあまり含まれていない場合、圧縮効果は明ら​​かではありません。

<<:  次世代産業用ロボットに対する人工知能(AI)の影響

>>:  人工知能の時代において、結核を根絶するまでにどれくらい時間がかかるのでしょうか?

ブログ    
ブログ    
ブログ    
ブログ    

推薦する

強化学習を使用して、顧客が注目する広告を選択する方法

[51CTO.com クイック翻訳] 現在、世界中のデジタル広告代理店は、ニュースサイト、検索エンジ...

音声対話とニューラルネットワークで構築された人工知能車両システム「WindLink 3.0」が正式に発売されました

明日のフライトとホテルを予約し、天気を確認する。このようなシナリオは誰もが経験したことがあると思いま...

...

PythonでAutoMLを実装する方法を教えます

[51CTO.com クイック翻訳] 機械学習は複雑な問題を自動的に解決する方法であることはすでに知...

...

上位 10 の古典的なソート アルゴリズムの詳細な説明: シェル ソート、マージ ソート、クイック ソート

[[378304]]上位 10 の古典的なソート アルゴリズム - シェル ソート、マージ ソート、...

...

機械学習: Python による予測

機械学習は基本的に、既存のデータを使用して新しいデータについて予測を行う人工知能のサブセットです。も...

5分で様々な人工知能技術を紹介

人工知能は、コンピューターが人間と同様のレベルの知能を発揮できるようにするさまざまな技術を網羅する幅...

武器化されたAIとIoT攻撃は最大の技術的脅威となる

1. 「企業が人工知能やモノのインターネットなどの新しいテクノロジーの導入を検討するにつれ、攻撃対象...

クアルコムのアモン社長:5G+AIがインテリジェントな接続の未来を切り開く

7月9日、2020年世界人工知能大会(WAIC)クラウドサミットが正式に開幕した。クアルコムのクリス...

...

金融規制当局が注意喚起:「AIによる顔の改変」などの新たな詐欺手法に注意

10月9日、近年、犯罪者が詐欺の手口を絶えず革新しており、金融消費者がそれを防ぐことが困難になってお...

5G車道協調自動運転技術の応用について解説した記事

自動運転は現在社会的なホットな話題となっており、人工知能と自動化技術の革新的な開発にとって重要な方向...

自動運転は飛躍的な進歩を遂げており、マスク氏は年内にL5レベルの自動運転が実現すると発言した。

自動運転技術は、世界中の大手自動車メーカーの主要な研究開発方向となっています。現在、多くの自動車メー...