web-dev-qa-db-ja.com

ビットごとの演算子のみを使用してビットカウントを実装するにはどうすればよいですか?

タスクは、ビット演算子のみを使用してビットカウントロジックを実装することです。私はそれをうまく機能させましたが、誰かがもっとエレガントなアプローチを提案できるかどうか疑問に思っています。

ビット演算のみが許可されます。 「if」、「for」などはありません

int x = 4;

printf("%d\n", x & 0x1);
printf("%d\n", (x >> 1) & 0x1);
printf("%d\n", (x >> 2) & 0x1);
printf("%d\n", (x >> 3) & 0x1);

ありがとうございました。

13
James Raitsev

から http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

unsigned int v; // count bits set in this (32-bit value)
unsigned int c; // store the total here

c = v - ((v >> 1) & 0x55555555);
c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
c = ((c >> 4) + c) & 0x0F0F0F0F;
c = ((c >> 8) + c) & 0x00FF00FF;
c = ((c >> 16) + c) & 0x0000FFFF;

編集:確かに、それは少し最適化されているため、読みにくくなっています。次のように読む方が簡単です。

c = (v & 0x55555555) + ((v >> 1) & 0x55555555);
c = (c & 0x33333333) + ((c >> 2) & 0x33333333);
c = (c & 0x0F0F0F0F) + ((c >> 4) & 0x0F0F0F0F);
c = (c & 0x00FF00FF) + ((c >> 8) & 0x00FF00FF);
c = (c & 0x0000FFFF) + ((c >> 16)& 0x0000FFFF);

これらの5つの各ステップでは、隣接するビットを1、2、4などのグループに追加します。この方法は分割統治法に基づいています。

最初のステップでは、ビット0と1を足し合わせて、結果を2ビットセグメント0-1に入れ、ビット2と3を足して、結果を2ビットセグメント2-3に入れます。

2番目のステップでは、2ビットの0-1と2-3を足し合わせて、結果を4ビットの0-3に入れ、2ビットの4-5と6-7を足して、結果を4ビットに入れます。 4-7など..

例:

So if I have number 395 in binary 0000000110001011 (0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 1)
After the first step I have:      0000000101000110 (0+0 0+0 0+0 0+1 1+0 0+0 1+0 1+1) = 00 00 00 01 01 00 01 10
In the second step I have:        0000000100010011 ( 00+00   00+01   01+00   01+10 ) = 0000 0001 0001 0011
In the fourth step I have:        0000000100000100 (   0000+0001       0001+0011   ) = 00000001 00000100
In the last step I have:          0000000000000101 (       00000001+00000100       )

これは5に等しく、これは正しい結果です

30
iniju

事前に計算された配列を使用します

uint8_t set_bits_in_byte_table[ 256 ];

このテーブルのi番目のエントリは、設定されたビット数をバイトiに格納します。 set_bits_in_byte_table[ 100 ] = 33つあるので1 10進数の100のバイナリ表現のビット(= 0x64 = 0110-0100)。

それならやってみます

size_t count_set_bits( uint32_t x ) {
    size_t count = 0;
    uint8_t * byte_ptr = (uint8_t *) &x;
    count += set_bits_in_byte_table[ *byte_ptr++ ];
    count += set_bits_in_byte_table[ *byte_ptr++ ];
    count += set_bits_in_byte_table[ *byte_ptr++ ];
    count += set_bits_in_byte_table[ *byte_ptr++ ];
    return count;
}
2
Arun

いくつかの興味深い解決策 ここ

上記の解決策が退屈すぎる場合は、条件テストまたはループを免除されたC再帰バージョンを次に示します。

  int z(unsigned n, int count);
  int f(unsigned n, int count);

  int (*pf[2])(unsigned n, int count) = { z,f };

  int f(unsigned n, int count)
  {
     return (*pf[n > 0])(n >> 1, count+(n & 1));
  }

  int z(unsigned n, int count)
  {
     return count;
  }

  ...
  printf("%d\n", f(my_number, 0));
1
Ring Ø

answer の簡単な図を次に示します。

a b c d       0 a b c       0 b 0 d    
&             &             +
0 1 0 1       0 1 0 1       0 a 0 c
-------       -------       -------
0 b 0 d       0 a 0 c       a+b c+d

したがって、a + bを格納するための正確な2ビットと、c + dを格納するための2ビットがあります。 a = 0、1などなので、2ビットがそれらの合計を格納するために必要なものです。次のステップでは、2ビット値の合計などを格納するための4ビットがあります。

1
irudyak