数7を表す8ビットは、次のようになります。
00000111
3ビットが設定されています。
32ビット整数の設定ビット数を決定するためのアルゴリズムは何ですか?
これは ' Hamming Weight '、 'popcount'、または 'sideways Addition'として知られています。
「最善」のアルゴリズムは、実際にはどのCPUを使用しているか、またどのような使用パターンかによって異なります。
それを実行するための単一の組み込み命令を持っているCPUもあれば、ビットベクトルに作用する並列命令を持っているCPUもあります。並列命令(x86のpopcnt
のように、サポートされているCPU上)はほぼ確実に最速になります。他のアーキテクチャの中には、1サイクルあたりのビットをテストするマイクロコード化ループを使って遅い命令を実装するものもあります( 引用が必要 )。
CPUに大容量のキャッシュがある場合や、これらの命令の多くを厳密なループで実行している場合は、事前入力されたテーブル検索方法が非常に高速になります。しかし、CPUがメインメモリからテーブルの一部をフェッチしなければならない「キャッシュミス」の費用が原因で、問題が発生する可能性があります。
あなたのバイトがほとんど0またはほとんど1になることを知っているなら、これらのシナリオのための非常に効率的なアルゴリズムがあります。
私は、非常に優れた汎用アルゴリズムは、「並列」または「可変精度SWARアルゴリズム」として知られる以下のものであると思います。私はこれをCのような疑似言語で表現しました、あなたはそれを特定の言語のために働くように調整する必要があるかもしれません(例えばC++のためのuint32_tとJavaの>>>の使用)。
int numberOfSetBits(int i)
{
// Java: use >>> instead of >>
// C or C++: use uint32_t
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
これは、説明したアルゴリズムの中で最悪の場合の動作が最も優れているので、使用パターンや使用した値を効率的に処理します。
このビット単位のSWARアルゴリズムは、SIMDを搭載したCPUでスピードアップするために、単一の整数レジスタではなく、一度に複数のベクトル要素で並列処理することができますが、ポップカウント命令は使用できません。 (Nehalem以降だけでなく、あらゆるCPUで動作する必要があるx86-64コードなど)
ただし、ポップカウントにベクトル命令を使用する最善の方法は、通常、可変シャッフルを使用して各バイトを一度に4ビットずつ並列にテーブルルックアップすることです。 (4ビットはベクトルレジスタに保持された16エントリテーブルをインデックスする)。
Intel CPUでは、ハードウェア64ビットpopcnt命令は SSSE3 PSHUFB
bit-parallel implementation よりも約2倍優れていますが、 コンパイラが正しい場合 のみです。そうでなければSSEはかなり先に出ることができます。新しいバージョンのコンパイラは popcnt false dependencyIntelの問題 を認識しています。
参考文献:
https://graphics.stanford.edu/~seander/bithacks.html
https://en.wikipedia.org/wiki/Hamming_weight
http://gurmeet.net/puzzles/fast-bit-counting-routines/
http://aggregate.ee.engr.uky.edu/MAGIC/#Population%20Count%20(Ones%20Count)
コンパイラの組み込み関数についても検討してください。
たとえばGNUコンパイラでは、次のようにします。
int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);
最悪の場合、コンパイラは関数の呼び出しを生成します。最良の場合、コンパイラは同じ仕事をより速くするためにcpu命令を発行します。
GCCの組み込み関数は、複数のプラットフォームでも機能します。ポップカウントはx86アーキテクチャーで主流になるので、今すぐ組み込み関数を使い始めるのは理にかなっています。他のアーキテクチャーは何年もの間人気があります。
X86では、同じ世代に追加されたベクタ命令も有効にするために、-mpopcnt
または-msse4.2
を使用してpopcnt
命令のサポートを想定できることをコンパイラに伝えることができます。 GCC x86のオプション を参照してください。 -march=nehalem
(または-march=
あなたのコードが想定し調整するCPUを問わず)は良い選択かもしれません。結果のバイナリを古いCPUで実行すると、不正命令フォルトが発生します。
バイナリをビルドするマシンに合わせてバイナリを最適化するには、(gcc、clang、またはICCと共に)-march=native
を使用します。
MSVCはx86 popcnt
命令の組み込み関数を提供します しかし、gccとは異なり、実際にはハードウェア命令の組み込み関数であり、ハードウェアサポートが必要です。
組み込みの代わりにstd::bitset<>::count()
を使う
理論的には、ターゲットCPUの効率的なポップカウント方法を知っているコンパイラは、ISO C++ std::bitset<>
を介してその機能を公開する必要があります。実際には、ターゲットCPUによっては、ビットハックAND/shift/ADDを使用した方がよい場合があります。
ハードウェアポップカウントがオプションの拡張であるターゲットアーキテクチャ(x86など)では、すべてのコンパイラが利用可能なときにそれを利用するstd::bitset
を持っているわけではありません。たとえば、MSVCはコンパイル時にpopcnt
サポートを有効にする方法がなく、/Ox /Arch:AVX
(SSE4.2を意味しますが、技術的にはpopcnt
のための別の機能ビットがあります)でも、常に テーブル検索 を使用します。
しかし、少なくともあなたはどこにでも動くポータブルなものを手に入れ、gcc/clangと正しいターゲットオプションを使えば、それをサポートするアーキテクチャのためのハードウェアポップカウントを手に入れることができます。
#include <bitset>
#include <limits>
#include <type_traits>
template<typename T>
//static inline // static if you want to compile with -mpopcnt in one compilation unit but not others
typename std::enable_if<std::is_integral<T>::value, unsigned >::type
popcount(T x)
{
static_assert(std::numeric_limits<T>::radix == 2, "non-binary type");
// sizeof(x)*CHAR_BIT
constexpr int bitwidth = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
// std::bitset constructor was only unsigned long before C++11. Beware if porting to C++03
static_assert(bitwidth <= std::numeric_limits<unsigned long long>::digits, "arg too wide for std::bitset() constructor");
typedef typename std::make_unsigned<T>::type UT; // probably not needed, bitset width chops after sign-extension
std::bitset<bitwidth> bs( static_cast<UT>(x) );
return bs.count();
}
Godboltコンパイラエクスプローラの gcc、clang、icc、およびMSVCのasm を参照してください。
x86-64 gcc -O3 -std=gnu++11 -mpopcnt
はこれを出します:
unsigned test_short(short a) { return popcount(a); }
movzx eax, di # note zero-extension, not sign-extension
popcnt rax, rax
ret
unsigned test_int(int a) { return popcount(a); }
mov eax, edi
popcnt rax, rax
ret
unsigned test_u64(unsigned long long a) { return popcount(a); }
xor eax, eax # gcc avoids false dependencies for Intel CPUs
popcnt rax, rdi
ret
PowerPC64のgcc -O3 -std=gnu++11
が出力します(int
argバージョン用)。
rldicl 3,3,0,32 # zero-extend from 32 to 64-bit
popcntd 3,3 # popcount
blr
このソースは、x86固有でもGNU固有でもありませんが、gcc/clang/iccを使用したx86用にのみコンパイルされます。
また、単一命令ポップカウントのないアーキテクチャに対するgccの代替は、一度に1バイトずつのテーブル検索です。これはすばらしいことではありません 例えばARMの場合 。
私の意見では、「最善の」解決策は、別のプログラマ(または2年後の元のプログラマ)が読むことができるものです。あなたはたぶんいくつかがすでに提供した最も速いか最も賢い解決策を望むかもしれません、しかし私はいつでも賢さより読みやすさを好みます。
unsigned int bitCount (unsigned int value) {
unsigned int count = 0;
while (value > 0) { // until all bits are zero
if ((value & 1) == 1) // check lower bit
count++;
value >>= 1; // shift bits, removing lower bit
}
return count;
}
もっとスピードが欲しいなら(そして後継者を手助けするためにそれをうまく文書化すると仮定して)、テーブル検索を使うことができます:
// Lookup table for fast calculation of bits set in 8-bit unsigned char.
static unsigned char oneBitsInUChar[] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F (<- n)
// =====================================================
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0n
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 1n
: : :
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // Fn
};
// Function for fast calculation of bits set in 16-bit unsigned short.
unsigned char oneBitsInUShort (unsigned short x) {
return oneBitsInUChar [x >> 8]
+ oneBitsInUChar [x & 0xff];
}
// Function for fast calculation of bits set in 32-bit unsigned int.
unsigned char oneBitsInUInt (unsigned int x) {
return oneBitsInUShort (x >> 16)
+ oneBitsInUShort (x & 0xffff);
}
これらは特定のデータ型サイズに依存しているため、移植性はありません。しかし、多くのパフォーマンス最適化は移植性がないため、問題にはならないかもしれません。あなたが移植性を望むならば、私は読みやすい解決策に固執するでしょう。
int pop(unsigned x)
{
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x = x + (x >> 8);
x = x + (x >> 16);
return x & 0x0000003F;
}
分岐なしで、最大20の命令で実行されます(Archに依存)。
ハッカーの喜び は 楽しいです!強くお勧めします。
ルックアップテーブルや popcount - を使わずに最速の方法は次のようになると思います。設定されたビットを12回の操作でカウントします。
int popcount(int v) {
v = v - ((v >> 1) & 0x55555555); // put count of each 2 bits into those 2 bits
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // put count of each 4 bits into those 4 bits
return c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}
これは、2つの半分に分割し、両方の半分のセットビット数をカウントしてからそれらを合計することによって、セットビットの合計数をカウントできるためです。 Divide and Conquer
パラダイムとも呼ばれます。詳細に入りましょう。
v = v - ((v >> 1) & 0x55555555);
2ビットのビット数は0b00
、0b01
または0b10
になります。 2ビットでこれを試してみましょう。
---------------------------------------------
| v | (v >> 1) & 0b0101 | v - x |
---------------------------------------------
0b00 0b00 0b00
0b01 0b00 0b01
0b10 0b01 0b01
0b11 0b01 0b10
これが要求されたものです:最後の列は2ビットのペアごとのセットビットの数を示しています。 2ビットの数値が>= 2 (0b10)
の場合、and
は0b01
を生成し、それ以外の場合は0b00
を生成します。
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
この記述は理解しやすいはずです。最初の操作の後、2ビットごとにセットビットのカウントがあります。ここで、4ビットごとにそのカウントを合計します。
v & 0b00110011 //masks out even two bits
(v >> 2) & 0b00110011 // masks out odd two bits
次に上記の結果を合計して、設定されたビットの総数を4ビットで求めます。最後の文は最もトリッキーです。
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
さらに分解しましょう...
v + (v >> 4)
これは2番目のステートメントに似ています。代わりに4のグループで設定されたビットを数えています。これまでの操作のおかげで、すべてのニブルにセットビットの数があることがわかりました。例を見てみましょう。バイト0b01000010
があるとします。これは、最初のニブルの4ビットセットと2番目のニブルの2ビットセットです。今、私たちはそれらのニブルを一緒に加えます。
0b01000010 + 0b01000000
最初のニブル0b01100010
で、バイト内の設定ビット数を取得します。したがって、数値内の全バイトの最後の4バイトをマスクします(それらを破棄します)。
0b01100010 & 0xF0 = 0b01100000
今各バイトはそれのセットビットのカウントを持っています。私たちはそれらを全部一緒にする必要があります。トリックは、興味深いプロパティを持つ0b10101010
で結果を乗算することです。私たちの数が4バイト、A B C D
であるならば、それはこれらのバイトA+B+C+D B+C+D C+D D
で新しい数をもたらすでしょう。 4バイト数には最大32ビットを設定できます。これは0b00100000
として表すことができます。
今必要なのは、すべてのバイトのすべての設定ビットの合計を持つ最初のバイトだけです。そして、それを>> 24
で取得します。このアルゴリズムは32 bit
ワード用に設計されていますが、64 bit
ワード用に簡単に変更できます。
たまたまJavaを使用している場合は、組み込みメソッドInteger.bitCount
がそれを実行します。
私は退屈し、3つのアプローチを10億回繰り返した。コンパイラはgcc -O3です。 CPUは、彼らが第一世代Macbook Proに入れたものは何でもです。
最も速いのは3.7秒の次のとおりです。
static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount( unsigned int i )
{
return( wordbits[i&0xFFFF] + wordbits[i>>16] );
}
2番目の場所は同じコードですが、2ハーフワードではなく4バイトを調べます。それは約5.5秒かかりました。
3番目の場所は8.6秒かかったちょっとした「横向きの足し算」アプローチです。
4位はGCCの__builtin_popcount()で、恥ずべき11秒です。
一度に1ビットずつ数えるアプローチは、ずっと遅く、そしてそれが完了するのを待つのにうんざりしました。
それであなたが何よりもパフォーマンスを気にしているのであればそれから最初のアプローチを使用してください。気にしていても、64 KBのRAMを費やすには不十分な場合は、2番目の方法を使用してください。それ以外の場合は、読み取り可能な(ただし遅い)1ビットずつの方法を使用してください。
あなたが少しいじるアプローチを使いたいと思う状況を考えるのは難しいです。
編集:同様の結果 ここ 。
unsigned int count_bit(unsigned int x)
{
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
x = (x & 0x0000FFFF) + ((x >> 16)& 0x0000FFFF);
return x;
}
このアルゴリズムを説明しましょう。
このアルゴリズムは、分割統治法アルゴリズムに基づいています。 8ビット整数213(2進数で11010101)があるとすると、アルゴリズムは次のように動作します(毎回2つの隣接ブロックをマージします)。
+-------------------------------+
| 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | <- x
| 1 0 | 0 1 | 0 1 | 0 1 | <- first time merge
| 0 0 1 1 | 0 0 1 0 | <- second time merge
| 0 0 0 0 0 1 0 1 | <- third time ( answer = 00000101 = 5)
+-------------------------------+
これはあなたのマイクロアーキテクチャーを知るのに役立つ質問の一つです。関数呼び出しのオーバーヘッドを排除するために-O3を使用してコンパイルされたgcc 4.3.3の下の2つのバリアントを、C++インラインを使用して10億回繰り返して計りました。正確なクロックサイクル)。
インラインint pop2(符号なしx、符号なしy) { x = x - ((x >> 1)&0x55555555); y = y - ((y >> 1)&0x55555555); x =(x&0x33333333)+((x >> 2)&0x33333333); y =(y&0x33333333)+(( y >> 2)&0x33333333); x =(x +(x >> 4))&0x0F0F0F0F; y =(y +(y >> 4))&0x0F0F0F0F [ x = x +(x >> 8); [= y] y = y +(y >> 8); [x] x = x +(x >> 16); [... 。] y = y +(y >> 16); return(x + y)&0x000000FF; }
未修正のHacker's Delightは12.2ギガサイクルを費やしました。私の並列版(2倍のビット数を数える)は13.0ギガサイクルで動作します。 2.4GHzコアデュオで合計10.5秒経過しました。 25ギガサイクル=このクロック周波数で10秒強なので、タイミングは正しいと確信しています。
これは命令の依存関係の連鎖と関係があり、これはこのアルゴリズムにとって非常に悪いものです。 1組の64ビットレジスタを使用することで、速度をさらにほぼ2倍にすることができました。実際、私が賢くてx + yをもう少し早く追加すれば、いくつかのシフトを削ることができます。いくつかの小さな調整を加えた64ビット版は偶数で出てくるでしょうが、再び2倍多くのビットを数えます。
128ビットのSIMDレジスタ、さらにもう2つの要素、およびSSE命令セットには、巧妙なショートカットもあります。
コードが特に透明になる理由はありません。インターフェースは単純で、アルゴリズムは多くの場所でオンラインで参照でき、包括的な単体テストにも適しています。それにつまずくプログラマーは何かを学ぶかもしれません。これらのビット操作はマシンレベルでは非常に自然です。
OK、私は調整された64ビット版をベンチにすることにしました。この1つのsizeof(unsigned long)== 8
インラインint pop2(符号なしlong x、符号なしlong y) { x = x - ((x >> 1)&0x5555555555555555); y = y - ((y >> 1)&0x5555555555555555); x =(x&0x333333333333333333)+((x >> 2)&0x3333333333333333); y =(y&0x33333333333333)+ ((y >> 2)&0x3333333333333333); x =(x +(x >> 4))&0x0F0F0F0F0F0F0F; y =(y +(y >> 4))&0x0F0F0F0F0F0F0F ; x = x + y。 [x] x = x +(x >> 8); [x] x = x +(x >> 16); [x] x = x +(x >> 32)。 return x&0xFF; }
これは正しいことです(私は注意深くテストしていません)これで、タイミングは10.70ギガサイクル/ 14.1ギガサイクルになります。その後の数字は1280億ビットになり、このマシンでは5.9秒が経過したことに相当します。私が64ビットモードで動いているので、非パラレルバージョンは少し速くなります、そしてそれは64ビットレジスタが32ビットレジスタよりわずかに良いのが好きです。
ここにもう少しOOOパイプライン処理があるかどうか見てみましょう。これはもう少し複雑なので、実際に少しテストしました。各項だけで合計64、合計256の合計になります。
インラインint pop4(符号なしlong x、符号なしlong y、 unsigned long u、符号なしlong v) { enum {m1 = 0x5555555555555555、[.____ m2 = 0x33333333333333333、[...] m3 = 0x0F0F0F0F0F0F0F、[...] m4 = 0x000000FF000000FF}、[...] [...] x = x - ((x >> 1)&m1)。 y = y - ((y >> 1)&m 1); u = u - ((u >> 1)&m 1); v = v - ( (v >> 1)&m 1); x =(x&m 2)+((x >> 2)&m 2); y =(y&m 2)+((y >> 2)&m 2); [____] u =(u&m 2)+((u >> 2)&m 2); [____] v =(v&m 2)+((v >>) 2)&m2); x = x + y; u = u + v; x =(x&m 3)+((x >> 4)&m 3); u =(u&m 3)+((u >> 4)&m 3); [。 ] x = x + u; x = x +(x >> 8); x = x +(x >> 16); x = x&m 4; x = x +(x >> 32); return x&0x000001FF; }
ちょっとわくわくしましたが、いくつかのテストでinlineキーワードを使用していなくても、gccが-O3を使用してインライントリックを実行していることがわかりました。 gccにトリックをやらせると、pop4()を10億回呼び出すと12.56ギガサイクルかかりますが、引数を定数式として折りたたむことにしました。さらに現実的な数値は、さらに30%高速化した場合の19.6gcのようです。私のテストループは今このようになっています、各引数がgccがトリックをするのを止めるのに十分に異なっていることを確かめること。
hitime b4 = rdtsc(); (符号なしlong i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ i) sum + = pop4(i、i ^ 1、〜 i、i | 1); hitime e4 = rdtsc();
8.17秒で合計された256億ビットが経過しました。 16ビットテーブルルックアップでベンチマークされているように、3200万ビットで1.02秒までうまくいきます。直接比較することはできません。他のベンチではクロック速度が得られないためですが、64KBのテーブル版では問題がないことを確認しました。
更新:明白なことをし、さらに4つの重複行を追加することによってpop6()を作成することにしました。 22.8gcになった、9.5秒で合計された3840億ビット。それで、32億ビットのために800msでもう20%があります。
なぜ2で繰り返し除算しないのですか?
(n%2)== 1の場合、n = 0である一方、n = 2の場合、n = 2である。 ]
私はこれが最速ではないことに同意しますが、「最善」は多少あいまいです。私は「最高の」は明快さの要素を持つべきだと主張します
あなたがビットパターンを書き出すとき、ハッカーの喜びのビット回転はとても明確になります。
unsigned int bitCount(unsigned int x)
{
x = ((x >> 1) & 0b01010101010101010101010101010101)
+ (x & 0b01010101010101010101010101010101);
x = ((x >> 2) & 0b00110011001100110011001100110011)
+ (x & 0b00110011001100110011001100110011);
x = ((x >> 4) & 0b00001111000011110000111100001111)
+ (x & 0b00001111000011110000111100001111);
x = ((x >> 8) & 0b00000000111111110000000011111111)
+ (x & 0b00000000111111110000000011111111);
x = ((x >> 16)& 0b00000000000000001111111111111111)
+ (x & 0b00000000000000001111111111111111);
return x;
}
最初のステップでは、偶数ビットを奇数ビットに加算して、2つのビットの合計を生成します。他のステップでは、最終カウントがint全体を占めるまで、高位チャンクを低位チャンクに追加して、チャンクサイズをずっと2倍にします。
2の間の幸せな媒体のために32 ルックアップテーブルと各ビットを個別に繰り返す:
int bitcount(unsigned int num){
int count = 0;
static int nibblebits[] =
{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
for(; num != 0; num >>= 4)
count += nibblebits[num & 0x0f];
return count;
}
それは最速または最善の解決策ではありませんが、私は私のやり方で同じ質問を見つけました、そして私は考え始めました。数学的な側面から問題を得てグラフを描けば、周期的な部分がある関数であることがわかり、周期間の違いがわかります。どうぞ:
unsigned int f(unsigned int x)
{
switch (x) {
case 0:
return 0;
case 1:
return 1;
case 2:
return 1;
case 3:
return 2;
default:
return f(x/4) + f(x%4);
}
}
これはO(k)
で行うことができます。ここでk
は設定されたビット数です。
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
あなたが探している関数は、しばしば "横向きの合計"または "人口数"の2進数と呼ばれます。 Knuthは、Fascicle 1A以前、11〜12ページで説明しています(ただし、第2巻、4.6.3〜(7)に簡単な説明はありましたが)。
locus classicusは、 ACMの通信、第3巻(1960年)、第5巻、322頁のPeter Wegnerの記事「バイナリコンピュータで1を数えるためのテクニック」です。 。彼はそこに二つの異なるアルゴリズムを与えている。一つは「まばらな」(すなわち、少数のものを持つ)と予想される数に対して最適化されたもの、そして反対の場合のためのものである。
private int get_bits_set(int v)
{
int c; // c accumulates the total bits set in v
for (c = 0; v>0; c++)
{
v &= v - 1; // clear the least significant bit set
}
return c;
}
未解決の質問: -
次のように負数をサポートするようにアルゴを修正することができます。
count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
count += 1
n /= 2
return count
2番目の問題を克服するために、次のようにアルゴを書くことができます -
int bit_count(int num)
{
int count=0;
while(num)
{
num=(num)&(num-1);
count++;
}
return count;
}
完全な参照のために見なさい:
http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html
もっと直感的な下記のコードを使います。
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
論理:n&(n-1)は、nの最後の設定ビットをリセットします。
P.S:面白い解決策ではあるが、これはO(1)解決策ではないことを私は知っている。
Brian Kernighanの メソッドも有用であると思います。設定されたビットと同数の反復を繰り返します。したがって、上位ビットのみが設定された32ビットのWordがある場合、それはループを1回だけ通過します。
int countSetBits(unsigned int n) {
unsigned int n; // count the number of bits set in n
unsigned int c; // c accumulates the total bits set in n
for (c=0;n>0;n=n&(n-1)) c++;
return c;
}
1988年に出版された、Cプログラミング言語第2版。 (Brian W. KernighanとDennis M. Ritchieによる)は、これを演習2-9で述べています。 2006年4月19日、Don Knuthはこの方法が「CACM 3(1960)、322でPeter Wegnerによって最初に発表されました。また、Derrick Lehmerによって独自に発見され、1964年にBeckenbachによって編集された本で発表されました。」
「最高のアルゴリズム」とはどういう意味ですか?ショートコードまたは断食コード?あなたのコードはとてもエレガントに見え、そしてそれは一定の実行時間を持ちます。コードも非常に短いです。
しかし、速度がコードサイズではなく主な要因であるならば、私は以下の方が速くなることができると思います:
static final int[] BIT_COUNT = { 0, 1, 1, ... 256 values with a bitsize of a byte ... };
static int bitCountOfByte( int value ){
return BIT_COUNT[ value & 0xFF ];
}
static int bitCountOfInt( int value ){
return bitCountOfByte( value )
+ bitCountOfByte( value >> 8 )
+ bitCountOfByte( value >> 16 )
+ bitCountOfByte( value >> 24 );
}
私はこれが64ビット値のためにより速くなることはないと思いますが、32ビット値はより速くなることができます。
c ++を使用している場合は、テンプレートメタプログラミングを使用することもできます。
// recursive template to sum bits in an int
template <int BITS>
int countBits(int val) {
// return the least significant bit plus the result of calling ourselves with
// .. the shifted value
return (val & 0x1) + countBits<BITS-1>(val >> 1);
}
// template specialisation to terminate the recursion when there's only one bit left
template<>
int countBits<1>(int val) {
return val & 0x1;
}
使い方は次のようになります。
// to count bits in a byte/char (this returns 8)
countBits<8>( 255 )
// another byte (this returns 7)
countBits<8>( 254 )
// counting bits in a Word/short (this returns 1)
countBits<16>( 256 )
もちろん、このテンプレートをさらに拡張してさまざまなタイプ(自動検出ビットサイズでも)を使用することもできますが、わかりやすくするために単純にしています。
編集:これは良い言及であることを忘れていました。なぜなら、C++コンパイラでは should が動作し、ビット数に定数値が使用されている場合は基本的にループをアンロールするためです(その他言葉、私はそれがあなたが見つける最も速い一般的な方法であると確信しています)
私は1990年頃にRISCマシン用の高速ビットカウントマクロを書きました。それは高度な算術演算(乗算、除算、%)、メモリフェッチ(遅すぎる)、分岐(遅すぎる)を使用していません。 32ビットのバレルシフタ(つまり、>> 1と>> 32は同じサイクル数を要します)。小さな定数(6、12、24など)は、レジスタにロードするのにコストがかからない、または格納されると仮定しています。一時的に再利用されます。
これらの仮定により、ほとんどのRISCマシンでは約16サイクル/命令で32ビットをカウントします。 15命令/サイクルは、加数を半分にするために少なくとも3命令(マスク、シフト、演算子)かかるため、サイクル数または命令数の下限に近いことに注意してください。したがって、log_2(32) = 5、5 x 3 = 15命令は準下限です。
#define BitCount(X,Y) \
Y = X - ((X >> 1) & 033333333333) - ((X >> 2) & 011111111111); \
Y = ((Y + (Y >> 3)) & 030707070707); \
Y = (Y + (Y >> 6)); \
Y = (Y + (Y >> 12) + (Y >> 24)) & 077;
これが、最初の最も複雑なステップの秘密です。
input output
AB CD Note
00 00 = AB
01 01 = AB
10 01 = AB - (A >> 1) & 0x1
11 10 = AB - (A >> 1) & 0x1
したがって、上の1列目(A)を取り、それを1ビット右にシフトし、それをABから減算すると、出力(CD)が得られます。 3ビットへの拡張も同様です。ご希望の場合は、上記の私のような8行ブールテーブルでそれをチェックすることができます。
私はいつもこれを競合プログラミングで使っています、そして書くのは簡単で効率的です:
#include <bits/stdc++.h>
using namespace std;
int countOnes(int n) {
bitset<32> b(n);
return b.count();
}
SIMD命令(SSSE3とAVX2)を使用して、配列内にビットカウントの実装を見つけました。 __popcnt64組み込み関数を使用する場合よりも2〜2.5倍優れたパフォーマンスを発揮します。
SSSE3バージョン:
#include <smmintrin.h>
#include <stdint.h>
const __m128i Z = _mm_set1_epi8(0x0);
const __m128i F = _mm_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m128i T = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
uint64_t BitCount(const uint8_t * src, size_t size)
{
__m128i _sum = _mm128_setzero_si128();
for (size_t i = 0; i < size; i += 16)
{
//load 16-byte vector
__m128i _src = _mm_loadu_si128((__m128i*)(src + i));
//get low 4 bit for every byte in vector
__m128i lo = _mm_and_si128(_src, F);
//sum precalculated value from T
_sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, lo)));
//get high 4 bit for every byte in vector
__m128i hi = _mm_and_si128(_mm_srli_epi16(_src, 4), F);
//sum precalculated value from T
_sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, hi)));
}
uint64_t sum[2];
_mm_storeu_si128((__m128i*)sum, _sum);
return sum[0] + sum[1];
}
AVX2バージョン:
#include <immintrin.h>
#include <stdint.h>
const __m256i Z = _mm256_set1_epi8(0x0);
const __m256i F = _mm256_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m256i T = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
uint64_t BitCount(const uint8_t * src, size_t size)
{
__m256i _sum = _mm256_setzero_si256();
for (size_t i = 0; i < size; i += 32)
{
//load 32-byte vector
__m256i _src = _mm256_loadu_si256((__m256i*)(src + i));
//get low 4 bit for every byte in vector
__m256i lo = _mm256_and_si256(_src, F);
//sum precalculated value from T
_sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, lo)));
//get high 4 bit for every byte in vector
__m256i hi = _mm256_and_si256(_mm256_srli_epi16(_src, 4), F);
//sum precalculated value from T
_sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, hi)));
}
uint64_t sum[4];
_mm256_storeu_si256((__m256i*)sum, _sum);
return sum[0] + sum[1] + sum[2] + sum[3];
}
私はフォーチュンファイルからのこの例が特に好きです。
#BITCOUNT(x)(((BX_(x)+(BX_(x)>> 4))&0x0F0F0F0F)%255)を定義します。 #BX_(x)を定義します((x) - ((((x)>> 1)&0x77777777) - ((((x)>> 2)&0x33333333) - (((x)>> 3)&0x11111111)))
それはとてもきれいだから私はそれが一番好きです!
Java JDK 1.5
Integer.bitCount(n);
nは、1がカウントされる数です。
またチェック
Integer.highestOneBit(n);
Integer.lowestOneBit(n);
Integer.numberOfLeadingZeros(n);
Integer.numberOfTrailingZeros(n);
//Beginning with the value 1, rotate left 16 times
n = 1;
for (int i = 0; i < 16; i++) {
n = Integer.rotateLeft(n, 1);
System.out.println(n);
}
これは、あらゆるアーキテクチャ上で各アルゴリズムをベンチマークできるポータブルモジュール(ANSI-C)です。
あなたのCPUは9ビットバイトですか?問題ありません:-)現時点では2つのアルゴリズム、K&Rアルゴリズムとバイト単位のルックアップテーブルを実装しています。ルックアップテーブルは、K&Rアルゴリズムよりも平均で3倍高速です。誰かが "Hacker's Delight"アルゴリズムを移植可能にする方法を考え出すことができれば、それを追加しても構いません。
#ifndef _BITCOUNT_H_
#define _BITCOUNT_H_
/* Return the Hamming Wieght of val, i.e. the number of 'on' bits. */
int bitcount( unsigned int );
/* List of available bitcount algorithms.
* onTheFly: Calculate the bitcount on demand.
*
* lookupTalbe: Uses a small lookup table to determine the bitcount. This
* method is on average 3 times as fast as onTheFly, but incurs a small
* upfront cost to initialize the lookup table on the first call.
*
* strategyCount is just a placeholder.
*/
enum strategy { onTheFly, lookupTable, strategyCount };
/* String represenations of the algorithm names */
extern const char *strategyNames[];
/* Choose which bitcount algorithm to use. */
void setStrategy( enum strategy );
#endif
。
#include <limits.h>
#include "bitcount.h"
/* The number of entries needed in the table is equal to the number of unique
* values a char can represent which is always UCHAR_MAX + 1*/
static unsigned char _bitCountTable[UCHAR_MAX + 1];
static unsigned int _lookupTableInitialized = 0;
static int _defaultBitCount( unsigned int val ) {
int count;
/* Starting with:
* 1100 - 1 == 1011, 1100 & 1011 == 1000
* 1000 - 1 == 0111, 1000 & 0111 == 0000
*/
for ( count = 0; val; ++count )
val &= val - 1;
return count;
}
/* Looks up each byte of the integer in a lookup table.
*
* The first time the function is called it initializes the lookup table.
*/
static int _tableBitCount( unsigned int val ) {
int bCount = 0;
if ( !_lookupTableInitialized ) {
unsigned int i;
for ( i = 0; i != UCHAR_MAX + 1; ++i )
_bitCountTable[i] =
( unsigned char )_defaultBitCount( i );
_lookupTableInitialized = 1;
}
for ( ; val; val >>= CHAR_BIT )
bCount += _bitCountTable[val & UCHAR_MAX];
return bCount;
}
static int ( *_bitcount ) ( unsigned int ) = _defaultBitCount;
const char *strategyNames[] = { "onTheFly", "lookupTable" };
void setStrategy( enum strategy s ) {
switch ( s ) {
case onTheFly:
_bitcount = _defaultBitCount;
break;
case lookupTable:
_bitcount = _tableBitCount;
break;
case strategyCount:
break;
}
}
/* Just a forwarding function which will call whichever version of the
* algorithm has been selected by the client
*/
int bitcount( unsigned int val ) {
return _bitcount( val );
}
#ifdef _BITCOUNT_EXE_
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Use the same sequence of pseudo random numbers to benmark each Hamming
* Weight algorithm.
*/
void benchmark( int reps ) {
clock_t start, stop;
int i, j;
static const int iterations = 1000000;
for ( j = 0; j != strategyCount; ++j ) {
setStrategy( j );
srand( 257 );
start = clock( );
for ( i = 0; i != reps * iterations; ++i )
bitcount( Rand( ) );
stop = clock( );
printf
( "\n\t%d psudoe-random integers using %s: %f seconds\n\n",
reps * iterations, strategyNames[j],
( double )( stop - start ) / CLOCKS_PER_SEC );
}
}
int main( void ) {
int option;
while ( 1 ) {
printf( "Menu Options\n"
"\t1.\tPrint the Hamming Weight of an Integer\n"
"\t2.\tBenchmark Hamming Weight implementations\n"
"\t3.\tExit ( or cntl-d )\n\n\t" );
if ( scanf( "%d", &option ) == EOF )
break;
switch ( option ) {
case 1:
printf( "Please enter the integer: " );
if ( scanf( "%d", &option ) != EOF )
printf
( "The Hamming Weight of %d ( 0x%X ) is %d\n\n",
option, option, bitcount( option ) );
break;
case 2:
printf
( "Please select number of reps ( in millions ): " );
if ( scanf( "%d", &option ) != EOF )
benchmark( option );
break;
case 3:
goto EXIT;
break;
default:
printf( "Invalid option\n" );
}
}
EXIT:
printf( "\n" );
return 0;
}
#endif
設定されたビットを数えるための多くのアルゴリズムがあります。しかし、私は最高のものが速いものだと思います!あなたはこのページの詳細を見ることができます:
私はこれを提案します:
64ビット命令を使用して14、24、または32ビットワードで設定されたビットをカウントする
unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
// option 1, for at most 14-bit values in v:
c = (v * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;
// option 2, for at most 24-bit values in v:
c = ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL)
% 0x1f;
// option 3, for at most 32-bit values in v:
c = ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) %
0x1f;
c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
この方法では、効率的にするために高速モジュラス除算機能を備えた64ビットCPUが必要です。最初のオプションは3つの操作だけを取ります。 2番目のオプションは10です。そして3番目のオプションは15かかります。
入力サイズで分岐して、事前に計算されたバイトビットテーブルを使用した高速C#ソリューション。
public static class BitCount
{
public static uint GetSetBitsCount(uint n)
{
var counts = BYTE_BIT_COUNTS;
return n <= 0xff ? counts[n]
: n <= 0xffff ? counts[n & 0xff] + counts[n >> 8]
: n <= 0xffffff ? counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff]
: counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff] + counts[(n >> 24) & 0xff];
}
public static readonly uint[] BYTE_BIT_COUNTS =
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
}
32ビットかどうか" コーディングインタビューのクラック "第4版演習5.5(第5章:ビット操作)を読んだ後、私はこのメソッドをJavaで使用しました。最下位ビットが1である場合、count
は整数を右シフトします。
public static int bitCount( int n){
int count = 0;
for (int i=n; i!=0; i = i >> 1){
count += i & 1;
}
return count;
}
どんなに速くても、これは定数0x33333333のソリューションよりも直感的です。それはあなたの "最良のアルゴリズム"の定義次第です。
できることは
while(n){ n=n&(n-1); count++; }
この背後にある論理は、n-1のビットがnの右端の設定ビットから反転していることです。 n = 6、すなわち110の場合、5は101であり、ビットはnの最も右側に設定されたビットから反転される。したがって、この2つの場合、繰り返しのたびに右端のビットを0にして、次に右端の設定ビットに移動します。設定ビットをカウントします。最悪時の複雑度はO(logn)です。すべてのビットが設定されています。
個人的にはこれを使います。
public static int myBitCount(long L){
int count = 0;
while (L != 0) {
count++;
L ^= L & -L;
}
return count;
}
int bitcount(unsigned int n)
{
int count=0;
while(n)
{
count += n & 0x1u;
n >>= 1;
}
return count;
}
反復 'count'は総ビット数に比例して時間内に実行されます。それは単純にすべてのビットをループし、while条件のためにわずかに早く終了します。 1の場合、または設定されたビットが スパース で、かつ 最下位ビット の場合に役立ちます。
BMI2対応のCPUを使用している場合は、もう1つのハミング重みアルゴリズム
the_weight=__tzcnt_u64(~_pext_u64(data[i],data[i]));
楽しむ!
__builtin_popcount()という名前の組み込み関数を使用できます。 C++にはno__builtin_popcountがありますが、これはGCCコンパイラの組み込み関数です。設定したビット数を整数で返します。
int __builtin_popcount (unsigned int x);
参照: ビット回転ハック
int countBits(int x)
{
int n = 0;
if (x) do n++;
while(x=x&(x-1));
return n;
}
または:
int countBits(int x) { return (x)? 1+countBits(x&(x-1)): 0; }
Java 8または9では、Integer.bitCount
を呼び出すだけです。
ビットフィールドを使用した、これまでに述べたことのない解決策があります。次のプログラムは、4つの異なる方法を使用して、100000000 16ビット整数の配列内の設定ビットをカウントします。タイミング結果は括弧内に示されています(MacOSXではgcc -O3
)。
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 100000000
typedef struct {
unsigned char bit0 : 1;
unsigned char bit1 : 1;
unsigned char bit2 : 1;
unsigned char bit3 : 1;
unsigned char bit4 : 1;
unsigned char bit5 : 1;
unsigned char bit6 : 1;
unsigned char bit7 : 1;
} bits;
unsigned char sum_bits(const unsigned char x) {
const bits *b = (const bits*) &x;
return b->bit0 + b->bit1 + b->bit2 + b->bit3 \
+ b->bit4 + b->bit5 + b->bit6 + b->bit7;
}
int NumberOfSetBits(int i) {
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
#define out(s) \
printf("bits set: %lu\nbits counted: %lu\n", 8*LENGTH*sizeof(short)*3/4, s);
int main(int argc, char **argv) {
unsigned long i, s;
unsigned short *x = malloc(LENGTH*sizeof(short));
unsigned char lut[65536], *p;
unsigned short *ps;
int *pi;
/* set 3/4 of the bits */
for (i=0; i<LENGTH; ++i)
x[i] = 0xFFF0;
/* sum_bits (1.772s) */
for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=sum_bits(*p++));
out(s);
/* NumberOfSetBits (0.404s) */
for (i=LENGTH*sizeof(short)/sizeof(int), pi=(int*)x, s=0; i--; s+=NumberOfSetBits(*pi++));
out(s);
/* populate lookup table */
for (i=0, p=(unsigned char*) &i; i<sizeof(lut); ++i)
lut[i] = sum_bits(p[0]) + sum_bits(p[1]);
/* 256-bytes lookup table (0.317s) */
for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=lut[*p++]);
out(s);
/* 65536-bytes lookup table (0.250s) */
for (i=LENGTH, ps=x, s=0; i--; s+=lut[*ps++]);
out(s);
free(x);
return 0;
}
ビットフィールドバージョンは非常に読みやすくなっていますが、タイミング結果はNumberOfSetBits()
より4倍以上遅いことを示しています。ルックアップテーブルベースの実装は、特に65 kBのテーブルでは、まだかなり高速です。
これは便利なサンプルコードです。
private static final int[] bitCountArr = new int[]{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
private static final int firstByteFF = 255;
public static final int getCountOfSetBits(int value){
int count = 0;
for(int i=0;i<4;i++){
if(value == 0) break;
count += bitCountArr[value & firstByteFF];
value >>>= 8;
}
return count;
}
C++ 20 std::popcount
次の提案はマージされました http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html に追加する必要があります<bit>
ヘッダー。
私は使用法が次のようになることを期待しています:
#include <bit>
#include <iostream>
int main() {
std::cout << std::popcount(0x55) << std::endl;
}
GCCにサポートが到着したら試してみます。GCC9.1.0のg++-9 -std=c++2a
はまだサポートしていません。
提案は言う:
ヘッダー:
<bit>
namespace std { // 25.5.6, counting template<class T> constexpr int popcount(T x) noexcept;
そして:
template<class T> constexpr int popcount(T x) noexcept;
制約:Tは符号なし整数型です(3.9.1 [basic.fundamental])。
戻り値:xの値の1ビットの数。
std::rotl
およびstd::rotr
も追加され、循環ビット回転を実行します。 C++での循環シフト(回転)操作のベストプラクティス
#!/user/local/bin/Perl
$c=0x11BBBBAB;
$count=0;
$m=0x00000001;
for($i=0;$i<32;$i++)
{
$f=$c & $m;
if($f == 1)
{
$count++;
}
$c=$c >> 1;
}
printf("%d",$count);
ive done it through a Perl script. the number taken is $c=0x11BBBBAB
B=3 1s
A=2 1s
so in total
1+1+3+3+3+2+3+3=19
私はこのアプローチをどこでも見たことがありません。
int nbits(unsigned char v) {
return ((((v - ((v >> 1) & 0x55)) * 0x1010101) & 0x30c00c03) * 0x10040041) >> 0x1c;
}
バイトごとに動作するので、32ビット整数の場合は4回呼び出す必要があります。これは横方向の加算から派生したものですが、命令数を7個に減らすために2つの32ビット乗算を使用しています。
ほとんどの現在のCコンパイラは、要求の数が4の倍数であることが明らかで、かなり競争力がある場合は、SIMD(SSE2)命令を使ってこの機能を最適化します。移植性があり、マクロまたはインライン関数として定義でき、データテーブルは不要です。
このアプローチは、64ビット乗算を使用して、一度に16ビットで動作するように拡張することができます。ただし、16ビットすべてが設定されると失敗し、ゼロが返されるため、0xffff入力値が存在しない場合にのみ使用できます。また、64ビット演算のために遅くなり、最適化されません。
これはPHPで機能するものです(すべてのPHP整数は32ビット符号付き、つまり31ビットです)。
function bits_population($nInteger)
{
$nPop=0;
while($nInteger)
{
$nInteger^=(1<<(floor(1+log($nInteger)/log(2))-1));
$nPop++;
}
return $nPop;
}
整数をバイナリ文字列に変換して数えるのはどうですか。
phpソリューション:
substr_count( decbin($integer), '1' );
設定されたビット数を数えるための簡単なアルゴリズム:
int countbits(n){
int count = 0;
while(n != 0){
n = n & (n-1);
count++;
}
return count;
}
11の例(1011)を取り、手動でアルゴリズムを実行してみてください。あなたに大いに役立つはずです!
少量のビットでうまく動作するはずの簡単な方法は、次のようになります(この例では4ビットの場合)。
(i&1)+(i&2)/ 2 +(i&4)/ 4 +(i&8)/ 8
他の人は簡単な解決策として少数のビットのためにこれを推薦しますか?