数え上げ符号
NビットのうちM個が'1'となる符号を考えましょう。このような符号の列挙を『数え上げ符号』といいます。研究でこれが必要になったのだけど、実装例がやたら少なくて少々困ってもうた。少ない資料を基にエンコード・デコードを書いたので貼っておきます。どうぞご参考に(責任はとりません)。
const int L=16;//コードワード長。32bitsだと17あたりが組み合わせ数の限界?。 //可能であればルックアップテーブル化がオススメ inline unsigned int choose(unsigned int n,unsigned int r) { return (n>=r)?comb(n,r):0; } //エンコード(x:コードワード、r:1の数) unsigned int enumerate(unsigned int x,int r) { unsigned int y=0; for(int k=L-1;k>=0;k--) if((x>>k)&1) y+=choose(k,r--); return y; } //デコード(y:数え上げ符号、r:1の数) unsigned int enumerateInv(unsigned int y,int r) { unsigned int x=0; for(int k=L-1;k>=0;k--) { if(y<choose(k,r)) continue; x|=(1<<k); y-=choose(k,r); r--; } return x; }
ただし、comb(n,r)は組み合わせの数を返す関数です。引数の値が小さいのであれば、comb()やchoose()はテーブル参照に置き換えた方が断然高速です。ちゃんとした測定はしてませんが、処理時間が半分近くに減少しました。
参考資料
- 数え上げ符号 - Wikipedia
- 意外にも英語のページがないみたいですね。
- Practical Entropy-Compressed Rank/Select Dictionary / 岡野原さん(PPT)
- エンコードの方だけ記述してありましたので、これを基にデコードを作りました。