May 09, 2004

速く美しいビットカウントアルゴリズム

Posted at May 9, 2004 11:52 PM in .

2chでおもしろいものを見つけた。

//非常に良くないビットカウントアルゴリズム inline unsigned int fuckinBitCount(unsigned int i) {  unsigned int cnt = 0;  for (int j = 0; j < 32; j++)   cnt += (i >>= 1) & 0x1;  return cnt; }
//速く美しいビットカウントアルゴリズム inline unsigned int excellentBitCount(unsigned int i) {   i = ((i >> 1) & 0x55555555) + (i & 0x55555555);   i = ((i >> 2) & 0x33333333) + (i & 0x33333333);   i = ((i >> 4) & 0x0F0F0F0F) + (i & 0x0F0F0F0F);   i = ((i >> 8) & 0x00FF00FF) + (i & 0x00FF00FF);   return (i >> 16) + (i & 0x0000FFFF); }

こんなものでビットが数えられるのかよ……と動かしてみると、なるほどばっちりではないですか。でもどうしてこんなコードが機能するんでしょう。とっても不思議です。こういうときには逆から辿ってみるのが定石。やってみよう。

先ず「return (i >> 16) + (i & 0x0000FFFF);」を見る。この行では上16bitと下16bitを整数型として足し合わせている。なので、この時点でビット数は上16bitと下16bitに分けてカウントされているということになる。

次に「i = ((i >> 8) & 0x00FF00FF) + (i & 0x00FF00FF);」を見る。んん。ここから分かりにくくなるのね。2バイト目と4バイト目をマスクして、片方は8bitシフトして足し合わせている。つまり、4バイトを4つのchar(8bitの整数型)と見なして、1つ目と2つ目を足したものを上16bitに、3つ目と4つ目を足したものを16bitに格納している。ああ、なるほど。なんとなく見えてきたような気がする。

「i = ((i >> 4) & 0x0F0F0F0F) + (i & 0x0F0F0F0F);」を見る。これも前の行と同じ考え方だ。4バイトを4bitの整数型×8個として、隣り合う2つずつをセットにして足し合わせている。なるほどね。これをやつは考えた頭良いなあ。

つまり、32bitあるのを1bitずつ順番にカウントする(fuckinBitCount)のではなく、最初のステップで2bitずつをカウントし、次のステップではその結果を足し合わせることで4bitずつカウントし……って繰り返していくことで、全体として少ない手数でビットをカウントできている(excellentBitCount)。分割統治ってやつだ。実際、この2つのビットカウントアルゴリズムの関係はバブルソートとクイックソートの関係そのままで、ある考え方を他の問題に応用することの偉大さみたいなものを感じてしまった(ちょっと大仰かもしれんけど)。こういうコードを考えつくやつを心から尊敬します。



Trackback

You can ping this entry by using http://windy.ac/MT/mt-tb.cgi/363 .

Comments

Post a comment










Remember personal info?