0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

次数1の静的huffman符号

0
Posted at

次数1の静的huffman符号による圧縮programを紹介します。これはつまり1文字前の文字でhuffman符号を切り替えるというものです。適応型huffman符号ではお馴染みの手法ですが、静的huffman符号で実装するのは厄介です。
と言うのも圧縮文に符号長、あるいはhuffman木自体を刻印しなければならず、最大で256文字分 x 256個となり、工夫しないと大幅な圧縮率の低下を招くからです。
今回はとっても古典的なhuffman木刻印版のようなものを実装。存分に、なんなりと期待しないで下さい。

実装編

JavaScriptで書き散らかしました。

/*圧縮関数
@In: Array/Uint8Array. 圧縮元配列
@return: Array
*/
function o1HuffEncode(In){
	//bit書き込み
	function putBits(bl,bn){
		for(;bl>=bitc;bitc=8)
			Out[op++]=Bits|(1<<bitc)-1&bn>>(bl-=bitc),Bits=0;Bits|=((1<<bl)-1&bn)<<(bitc-=bl)
	}
	//huffman木刻印
	function writeTree(c,Left,Right,Mark,Use){
		if(c<256){//記号はCBT符号化
			let a=Use[c],b=0;
			for(Mark[a]=1;a;)b+=!Mark[--a];
			for(;use>>++a;);
			c=(1<<a)-use--;b<c||(++a,b+=c);
			putBits(a,b)
		}else putBits(1,1),
			writeTree(Left[c],Left,Right,Mark,Use),
			writeTree(Right[c],Left,Right,Mark,Use)
	}
	function downheap(i,Heap,Hit,s){
		for(var j,k=Heap[i];(j=2*i)<=s&&Hit[k]>Hit[Heap[j<s&&Hit[Heap[j]]>Hit[Heap[j+1]]&&++j||j]];Heap[i]=Heap[i=j]);
		Heap[i]=k
	}
	function makeTree(Hit,Use){
		var a=-1,c,j,k,s=0,v=255,Heap=[,0],Dad=[],Left=[],Right=[];
		//heapを構築
		for(;a<v;)if(Hit[++a])Heap[++s]=a;
		for(a=s>>1;a;)downheap(a--,Heap,Hit,s);

		//huffman木を構築(最小頻度の2つのnodeを結合)
		for(k=Heap[1];s>1;Dad[Right[k]=j]=-k)
			a=Heap[1], Heap[1]=Heap[s--], downheap(1,Heap,Hit,s),
			Hit[k=++v]=Hit[a]+Hit[j=Heap[1]],
			Left[Heap[1]=Dad[a]=k]=a, downheap(1,Heap,Hit,s);

		//huffman符号を生成
		for(j=0;(s=j)<256;Hit[j]=c,Heap[j++]=v)
			for(c=v=0;s=Dad[s];v++)if(s<1)s=-s,c|=1<<v;
		use=cs;writeTree(k,Left,Right,[],Use);
		Hit.L=Heap
	}
	var a=0,bitc=8,Bits,c=0,d=0,size=In.length,op=0,use=-1,cs=0,
		o1=[],Out=[],Use=[];
	//write size
	for(;size>>++d;);
	putBits(5,d);putBits(d,size);
	//build context
	for(;a<size;o1[c][c=In[a++]]++,Use[c]=1)if(!o1[c])o1[c]=new Uint32Array(512);
	for(c in Use)if(!o1[c])(o1[c]=[])[c]=1;	// dummy
	//write exist context
	for(putBits(1,Use[a=0]);c=a<256;putBits(d*2-1,c)){
		for(d=!o1[a];d==!o1[++a]&&a<256;)c++;
		if(c<256)for(d=0;c>>++d;);
		else c=0,d=4.5
	}
	for(;++use<256;)Use[use]&&(Use[use]=cs++);
	//build huffman code
	for(a=c=d=0;c<256;c++)o1[c]&&makeTree(o1[c],Use);
	//huffman encoding
	for(;a<size;d=c)putBits(o1[d].L[c=In[a++]],o1[d][c]);
	if(Bits)Out[op]=Bits;
	return Out
}
/*展開関数
@In: Array/Uint8Array. 圧縮された配列
@return: Array
*/
function o1HuffDecode(In){
	//bit読み込み
	function getBit(){return bitc?Bits>>--bitc&1:(Bits=In[a++])>>(bitc=7)&1}
	function getBits(bl,bn){
		for(;bl>bitc;bitc=8)bn|=((1<<bitc)-1&Bits)<<(bl-=bitc),Bits=In[a++];
		return(1<<bl)-1&Bits>>(bitc-=bl)|bn
	}
	//huffman木読み込み
	function readTree(Tree,Mark,n){
		if(getBit()){
			Tree[n=c++<<1]=readTree(Tree,Mark);
			Tree[n|1]=readTree(Tree,Mark);
			return n>>1
		}//read new symbol
		let a=n=0,b;
		for(;use>>++a;);
		b=(1<<a)-use--;a=getBits(--a);
		for(a<b||(a+=a+getBit()-b);Mark[n]||a--;)n++;
		Mark[n]=1;
		return Use[n]
	}
	var a=0,bitc=0,Bits,size=getBits(getBits(5)),n,c=1,i=0,o=0,use,cs=getBits(1)-1,
		Out=[],Use=[],o1=[],Tree;
	//read exist context
	for(;i<256;c^=1){
		for(n=0;n<8&&!getBit();)n++;
		for(n=n<8?1<<n|getBits(n):256;n--;i++)if(c)o1[Use[cs++]=i]=[]
	}
	for(;++n<256;)if(o1[n])use=cs,c=256,o1[n].c=readTree(o1[n],[]);
	//huffman decoding
	for(n=0;o<size;Out[o++]=n=c)
		for(Tree=o1[n],c=Tree.c;c>255;)
			c=Tree[c<<1|(bitc?Bits>>--bitc&1:(Bits=In[a++])>>(bitc=7)&1)]
	return Out
}

中古技術の総動員という有り様。復号処理はhuffman符号を1bitずつ解読するという低速な仕様。codepenで遊ぶことも可能

See the Pen data compression by order1 static huffman by xezz (@xezz) on CodePen.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?