質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Groovy

Groovyは、Java用のオブジェクト指向型プログラミング言語です。PythonやRuby、Perl、そしてSmalltalkに似た特徴を有する動的な言語です。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

3078閲覧

ハフマン符号化のプログラムで問題

yukkuri

総合スコア624

Groovy

Groovyは、Java用のオブジェクト指向型プログラミング言語です。PythonやRuby、Perl、そしてSmalltalkに似た特徴を有する動的な言語です。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

0クリップ

投稿2019/01/29 14:25

前提・実現したいこと

現在、wikipediaを参考に、ハフマン符号化のプログラムをjavaで作っていました。
しかし、画像を読み込み、符号化するとこのようになってしまいます。

first size 73736 #ファイルのサイズ size 4194304 # 符号化後

該当のソースコード

Java

1package org.jyl.base.io; 2 3import java.util.ArrayList; 4import java.util.BitSet; 5import java.util.PriorityQueue; 6 7public class Huffman 8{ 9 private BitStream bs; 10 11 public Huffman( byte[] data ) 12 { 13 bs = new BitStream(); 14 CodeInfo[] cf = createValue2Code( data ); 15 16 compress( data, cf ); 17 } 18 19 CodeInfo[] createValue2Code( byte[] data ) 20 { 21 // 各バイト値の発生回数を数える 22 TreeNode[] nodes = new TreeNode[ 256 ]; 23 for( int l = 0; l < 256; l++ ){ 24 // groovy での new TreeNode(value:l) 25 nodes[ l ] = new TreeNode(); 26 nodes[ l ].value = (byte)l; 27 } 28 29 // data の中身分繰り返す 30 for( byte v : data ) nodes[ Byte.toUnsignedInt( v ) ].occurrence++; 31 32 // ハフマン木を作成 33 // 配列をリストに変換 34 ArrayList<TreeNode> nodeslist = new ArrayList<TreeNode>( 0 ); 35 for( int l = 0; l < nodes.length; l++ ) nodeslist.add( nodes[ l ] ); 36 37 PriorityQueue<TreeNode> queue = new PriorityQueue<TreeNode>( nodeslist ); 38 39 while( queue.size() > 1 ){ 40 TreeNode n1 = queue.poll(), n2 = queue.poll(); 41 TreeNode tn = new TreeNode(); 42 tn.occurrence = n1.occurrence + n2.occurrence; 43 tn.children = new ArrayList<TreeNode>( 0 ); 44 tn.children.add( n1 ); tn.children.add( n2 ); 45 queue.add( tn ); 46 } 47 48 TreeNode root = queue.poll(); 49 50 // 深さ優先探索でバイト値->符号情報を作成 51 CodeInfo[] value2Code = new CodeInfo[ 256 ]; 52 dfs( value2Code, root, 0, 0 ); 53 54 return value2Code; 55 } 56 57 void dfs( CodeInfo[] value2Code, TreeNode node, int code, int codeSize ) 58 { 59 if( node.children == null ){ 60 CodeInfo cf = new CodeInfo(); 61 cf.code = code; cf.codeSize = codeSize; 62 value2Code[ Byte.toUnsignedInt( node.value ) ] = cf; 63 }else{ 64 dfs( value2Code, node.children.get( 0 ), code << 1, codeSize + 1 ); 65 dfs( value2Code, node.children.get( 1 ), ( code << 1 ) + 1, codeSize + 1 ); 66 } 67 } 68 69 /** 70 * 圧縮する 71 */ 72 void compress( byte[] data, CodeInfo[] value2Code ) 73 { 74 for( byte v : data ){ 75 CodeInfo codeInfo = value2Code[ Byte.toUnsignedInt( v ) ]; 76 bs.write( codeInfo.code, codeInfo.codeSize ); 77 } 78 } 79 80 public int getSize() 81 { 82 return bs.bs.size(); 83 } 84 85 /** 86 * 符号情報のクラス 87 */ 88 class CodeInfo 89 { 90 int code, codeSize; 91 } 92 93 class TreeNode implements Comparable<TreeNode> 94 { 95 byte value = 0; int occurrence = 0; 96 ArrayList<TreeNode> children; 97 98 public int compareTo( TreeNode that ) 99 { 100 return this.occurrence - that.occurrence; 101 } 102 } 103 104 class BitStream 105 { 106 BitSet bs = new BitSet(); 107 int len = 0, pos = 0; 108 109 void write( int v, int bits ) 110 { 111 for( int l = 0; l < 256; l++ ) 112 bs.set( len++, ( ( v >>> l ) & 1 ) != 0 ); 113 } 114 115 int read( int bits ) 116 { 117 int v = 0; 118 119 for( int l = 0; l < bits; l++ ){ 120 if( bs.get( pos++ ) ) v |= 1 << l; 121 } 122 123 return v; 124 } 125 } 126} 127
import java.io.FileInputStream; import java.util.ArrayList; import org.jyl.base.io.Huffman; public class Test { public static void main( String[] args ) throws java.io.IOException { ArrayList<Byte> b = new ArrayList<Byte>( 0 ); FileInputStream fis = new FileInputStream( new java.io.File( "/home/pi/Downloads/image/nc74842.png" ) ); int data = 0; for( int l = 0; ( data = fis.read() ) != -1; l++ ){ b.add( (byte)data ); } byte[] by = new byte[ b.size() ]; for( int l = 0; l < by.length; l++ ){ by[ l ] = b.get( l ); } System.out.println( "first size " + by.length ); Huffman hf = new Huffman( by ); System.out.println( "size " + hf.getSize() / 8 ); } }

試したこと

何が原因でこうなっているのか全くわかりません。
Groovyの仕様を確認した

補足情報(FW/ツールのバージョンなど)

Java8 を使っています。

bochan2👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

momon-ga

2019/01/29 14:57

ファイルはpngじゃないと、ダメですか? ビットマップとかテキストなら大丈夫かと。
yukkuri

2019/01/30 04:12

最初、12バイトくらいのバイト配列で試していました。
guest

回答3

0

いきなり画像データを入れたりせず, 10バイト程度の簡単なバイト配列を作って手作業で結果を算出して, それから実際にコードに通して結果を比較したほうが良いと思います.

投稿2019/01/29 14:55

jimbe

総合スコア12632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukkuri

2019/01/30 04:12

すみません、書き忘れですね。最初、12バイトくらいのバイト配列で試していました。 しかし、うまくいかず、データを変えてみた、そして成功しない、というのが現状です。
guest

0

何が原因でこうなっているのか全くわかりません。

質問者さんへのアドバイスとして間違っている点を示すよりはどうやって期待通りに動作するプログラムを作るかの方が有効に思えます。

ご自分でどのくらいこの論理をデバッグしていますか?「全くわかりません」と言い切れるほどデバッグしておられるでしょうか?失礼ながらそうは思えません。

例えばikadzuchiさんご指摘のバグがなぜ見つけられなかったのか大いに反省するとよいと思います。

ご自分でもっとデバッグできることがあるはずです。変換前後の長さを比べておられますが、おそらく実際はもっといろいろデバッグして今のコードになっているのだろうと想像します。しかしまだデバッグのしかたが浅いと思います。コード中に出現する様々な情報(変数)の内容が妥当になっているかどうか細かく確認していくのがデバッグの基本的アプローチの一つです。「各情報がどうであるべきか」はわかるはずですのでそれをデバッグプリントするなりデバッガーで調べるなりして確認すればよいのです。もし期待と違っていたらその情報をどこで計算しているかさらに調べ犯人(バグ)を追い詰めていきます。

ということで「もっと気を入れてデバッグしましょう」が質問者さんにとって一番申し上げたいアドバイスです。

デバッグは発見的(heuristic)な作業ですので、「これこれの手順でやれば結果が得られる」というような単純な作業ではありません。ご自分で様々なバグと戦っているうちに方法論のあれこれが身についていく、そういうものです。アプローチは色々で「個々の論理が正しいか一つ一つ確認していく」だったり逆に「期待外の結果を見てその直接的な原因になりそうな部分から絞り込んでいく」など場合によって使い分けます。jimbeさんが回答しておられる「単純なケースを試してデバッグ過程で必要となる確認すべき情報量を小さくする」というのも非常に基本的かつ有効な手法と思います。


ただデバッグしましょうではコメントのかいがないので、ヒントだけ申し上げますとikadzuchiさんご指摘のバグに加え少なくとももう一点バグがあります。BitSetの仕様を質問者さんが勘違いしているというバグです。前述したようなデバッグをしていけば必ず見つかるはずですよ?見つけたらちょっとショックを受けるかも知れません。それくらい単純なバグです。


全てのPNGファイルがハフマン符号化によって短くなるとは限らないかも知れませんが、自分が適当な写真画像をハフマン符号化してみたところほんの少し短くなりました。

投稿2019/01/30 11:53

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2019/01/30 11:57

既に解決済みになってましたね。ただ自分が指摘しようとしたバグはまだ見つけておられないかもしれませんのでそれについても調べてみることをお勧めします。
ikadzuchi

2019/02/02 11:55

ああ、やはりまだ何かありましたか。計算が合わない(入力バイト数の256倍ビットになるはずが切り上げたような数)とは思ったもののそれ見つけられなかったんですよね。BitSetですか…。 減ったように報告されていたので大丈夫だったのかなと見逃してましたが、1種類で100->60バイトというのは確かにおかしいですね。 そしてPNGで縮みましたか。意外です。
KSwordOfHaste

2019/02/02 12:07

時間もたちましたし種明かしすると...size()メソッドの意味を勘違いしているのがバグです。実をいえば自分もすぐには気づけませんでした。自分もjimbeさんと同様の考え方で初期デバッグをする習慣ですが、new byte[] { 1, 2, 2, 3, 3, 3 } あたりのごく単純なものでデバッグしたところ明らかに結果のビット数が大きすぎるので「あれれ」と思ったらBitSet#sizeは「BitSetが内部的に用意しているバッファーのサイズ(ビット数)」を返すメソッドでした(リファレンスを見ないと気づけませんでした)。ご質問のコードではlenという変数に蓄積したビット数が保持されてますがBitSet#sizeの結果はあきらかにlenより大きいのでそのあたりはデバッグしていれば割合すぐに気づると思い、本回答を書いた次第です。
guest

0

ベストアンサー

とりあえず一番問題ありそうなのがここで、

Java

1void write( int v, int bits ) 2{ 3 for( int l = 0; l < 256; l++ ) 4 bs.set( len++, ( ( v >>> l ) & 1 ) != 0 ); 5}

入力ビット数によらず256ビット書き込んでいるように見えます。
ただこれでも計算が合わないので他に何かありそうです。

あとpngは既に圧縮されているので、正しいプログラムが書けたとしてもデータサイズは増えます。

投稿2019/01/29 15:07

ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukkuri

2019/01/30 05:52

たしかに、指摘された箇所を変更したら130000くらいになりました。
yukkuri

2019/01/30 06:01

どうやら、Jimbeさんもおっしゃっていたように、画像データを用いたのが原因だったようです。 100バイトで1種類のバイトに偏らせたデータを使用したところ、100->60バイトになりました。 ソースは、上で指摘されたものが原因だったようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問