表示数字7的8位如下所示:
00000111
设置三位。
什么算法可以确定32位整数中的设置位数?
答案 0 :(得分:814)
这称为“Hamming Weight”,“popcount”或“横向添加”。
“最佳”算法实际上取决于您所使用的CPU以及您的使用模式。
有些CPU只有一条内置指令可以执行此操作,而其他CPU则具有作用于位向量的并行指令。并行指令(如x86的popcnt
,在支持它的CPU上)几乎肯定会是最快的。其他一些架构可能会使用微编码循环实现慢速指令,每个循环测试一次(引用需要)。
如果您的CPU具有大缓存和/或您在紧密循环中执行大量这些指令,则预先填充的表查找方法可以非常快。然而,由于“缓存未命中”的代价,它可能会受到影响,其中CPU必须从主存储器中获取一些表。
如果你知道你的字节大部分是0或大部分是1,那么这些场景的算法非常有效。
我相信一个非常好的通用算法如下,称为“并行”或“可变精度SWAR算法”。我用C语言伪语言表达了这一点,您可能需要调整它以适用于特定语言(例如,在Java中使用uint32_t用于C ++和>>>):
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但没有可用的popcount指令的CPU上加速。 (例如x86-64代码必须在任何CPU上运行,而不仅仅是Nehalem或更高版本。)
然而,使用popcount的向量指令的最佳方法通常是使用变量shuffle在每个字节的并行时对4位进行表查找。 (4位索引保存在向量寄存器中的16个条目表。)
在Intel CPU上,硬件64位popcnt指令的性能优于SSSE3 PSHUFB
bit-parallel implementation约2倍,但仅为if your compiler gets it just right。否则SSE可能会显着提前。较新的编译器版本知道popcnt false dependency problem on Intel。
参考文献:
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)
答案 1 :(得分:203)
还要考虑编译器的内置函数。
在GNU编译器上,您可以使用:
int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);
在最坏的情况下,编译器将生成对函数的调用。在最好的情况下,编译器将发出一条cpu指令以更快地完成相同的工作。
GCC内在函数甚至可以跨多个平台工作。 Popcount将成为x86架构的主流,因此现在开始使用内在函数是有意义的。其他架构多年来一直都很流行。
在x86上,您可以告诉编译器它可以假定popcnt
或-mpopcnt
的{{1}}指令支持,以启用在同一代中添加的向量指令。见GCC x86 options。 -msse4.2
(或-march=nehalem
您希望代码假设和调整的CPU可能是一个不错的选择。在较旧的CPU上运行生成的二进制文件将导致非法指令错误。
要为您构建它们的机器优化二进制文件,请使用-march=
(使用gcc,clang或ICC)。
MSVC provides an intrinsic for the x86 popcnt
instruction,但与gcc不同,它实际上是硬件指令的固有内容,需要硬件支持。
使用-march=native
代替内置
理论上,任何知道如何有效地为目标CPU进行popcount的编译器都应该通过ISO C ++ std::bitset<>
公开该功能。在实践中,对于某些目标CPU,在某些情况下,你可能会更好地使用bit-hack AND / shift / ADD。
对于硬件popcount是可选扩展(如x86)的目标体系结构,并非所有编译器都有std::bitset<>::count()
在可用时利用它。例如,MSVC无法在编译时启用std::bitset
支持,并始终使用a table lookup,即使使用popcnt
(这意味着SSE4.2,尽管技术上有一个单独的功能位/Ox /arch:AVX
。)
但至少你得到的东西随处可见,而且gcc / clang有正确的目标选项,你可以获得支持它的架构的硬件popcount。
popcnt
请参阅Godbolt编译器资源管理器上的asm from gcc, clang, icc, and MSVC。
x86-64 #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();
}
发出此信息:
gcc -O3 -std=gnu++11 -mpopcnt
PowerPC64 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
发出(对于gcc -O3 -std=gnu++11
arg版本):
int
此源根本不是x86特定的或GNU特定的,但只能使用gcc / clang / icc很好地编译x86。
另请注意,gcc对没有单指令popcount的体系结构的回退是一次一个字节的表查找。这不是很好for ARM, for example。
答案 2 :(得分:173)
在我看来,“最佳”解决方案是另一位程序员(或两年后的原始程序员)可以在没有大量评论的情况下阅读的解决方案。你可能想要一些已经提供的最快或最聪明的解决方案,但我更喜欢可读性而不是聪明。
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);
}
虽然这些依赖于特定的数据类型大小,因此它们不具备可移植性。但是,由于许多性能优化无论如何都不可移植,这可能不是问题。如果你想要可移植性,我会坚持使用可读的解决方案。
答案 3 :(得分:95)
From Hacker's Delight, p. 66, Figure 5-2
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-ahh指令(取决于拱形),无分支。
Hacker's Delight 令人愉快!强烈推荐。
答案 4 :(得分:73)
我认为最快的方法 - 不使用查找表和 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;
}
它的工作原理是因为您可以通过分成两半来计算设置位的总数,计算两半中的设置位数,然后将它们相加。也称为Divide and Conquer
范例。让我们详细说明..
v = v - ((v >> 1) & 0x55555555);
两位中的位数可以是0b00
,0b01
或0b10
。让我们试着用2位来解决这个问题..
---------------------------------------------
| v | (v >> 1) & 0b0101 | v - x |
---------------------------------------------
0b00 0b00 0b00
0b01 0b00 0b01
0b10 0b01 0b01
0b11 0b01 0b10
这是所需要的:最后一列显示每两位对中的设置位数。如果两位数为>= 2 (0b10)
,则and
生成0b01
,否则生成0b00
。
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
这个陈述应该很容易理解。在第一次操作之后,我们每两位有一个设置位的计数,现在我们总计每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)
它类似于第二个陈述;我们正在计算4个组中的设置位。我们知道 - 因为我们以前的操作 - 每个半字节都有其中的设置位数。让我们看一个例子。假设我们有字节0b01000010
。这意味着第一个半字节设置了4比特,第二个设置了2比特。现在我们将这些小块一起添加。
0b01000010 + 0b01000000
它给出了第一个半字节0b01100010
中字节中设置位的计数,因此我们屏蔽了数字中所有字节的最后四个字节(丢弃它们)。
0b01100010 & 0xF0 = 0b01100000
现在每个字节都有其中的设置位数。我们需要将它们加在一起。诀窍是将结果乘以0b10101010
,它具有一个有趣的属性。如果我们的数字有四个字节A B C D
,则会产生一个带有这些字节A+B+C+D B+C+D C+D D
的新数字。一个4字节的数字最多可以设置32位,可以表示为0b00100000
。
我们现在需要的是第一个字节,其中包含所有字节中所有设置位的总和,我们通过>> 24
得到它。此算法专为32 bit
字设计,但可以轻松修改为64 bit
字。
答案 5 :(得分:54)
如果您正在使用Java,内置方法Integer.bitCount
将会这样做。
答案 6 :(得分:53)
我感到无聊,并计划了三十次迭代的三种方法。编译器是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] );
}
第二个位置使用相同的代码,但查找4个字节而不是2个半字。这需要大约5.5秒。
排在第三位的是“斜向加法”,耗时8.6秒。
第四名是GCC的__builtin_popcount(),这是一个可耻的11秒。
一次一位计数的方法慢得多,我厌倦了等待它完成。
因此,如果您关注的是性能高于其他所有,那么请使用第一种方法。如果您在意,但还不足以花费64Kb的RAM,请使用第二种方法。否则,使用可读(但缓慢)的一次一位方法。
很难想象你想要使用苦涩的方法。
修改:类似结果here。
答案 7 :(得分:30)
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;
}
让我解释一下这个算法。
该算法基于Divide and Conquer算法。假设有一个8位整数213(二进制11010101),算法就像这样(每次合并两个相邻块):
+-------------------------------+
| 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)
+-------------------------------+
答案 8 :(得分:28)
这是有助于了解您的微架构的问题之一。我只是使用C ++内联在gcc 4.3.3下使用-O3编译了两个变体来消除函数调用开销,十亿次迭代,保持所有计数的运行总和以确保编译器不会删除任何重要的东西,使用rdtsc进行计时(时钟周期精确)。
inline int pop2(unsigned x, unsigned 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 >> 8); x = x + (x >> 16); y = y + (y >> 16); return (x+y) & 0x000000FF; }
未修改的Hacker's Delight花了12.2万亿美元。我的并行版本(计数两倍的位数)运行在13.0 gigacycles中。在2.4GHz Core Duo上共同使用了10.5秒。 25个gigacycles =在这个时钟频率下超过10秒,所以我有信心我的时间是正确的。
这与指令依赖链有关,这对于该算法非常不利。通过使用一对64位寄存器,我几乎可以将速度提高一倍。事实上,如果我很聪明并且稍微增加x + y,我可以稍微减少一些变化。带有一些小调整的64位版本会出现关于偶数,但再次计算两倍的位数。
使用128位SIMD寄存器,另一个因子是2,SSE指令集通常也有聪明的快捷方式。
代码没有理由特别透明。界面简单,算法可以在很多地方在线参考,并且可以进行全面的单元测试。偶然发现它的程序员甚至可能会学到一些东西。这些位操作在机器级别非常自然。
好的,我决定对经过调整的64位版本进行测试。对于这个sizeof(无符号长)== 8
inline int pop2(unsigned long x, unsigned long y) { x = x - ((x >> 1) & 0x5555555555555555); y = y - ((y >> 1) & 0x5555555555555555); x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); y = (y & 0x3333333333333333) + ((y >> 2) & 0x3333333333333333); x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F; y = (y + (y >> 4)) & 0x0F0F0F0F0F0F0F0F; x = x + y; x = x + (x >> 8); x = x + (x >> 16); x = x + (x >> 32); return x & 0xFF; }
看起来是正确的(虽然我没有仔细测试)。现在时间是10.70千兆/ 14.1千兆。后来的数字总计1280亿比特,相当于这台机器上已经过了5.9秒。非并行版本加速了一点点因为我在64位模式下运行它喜欢64位寄存器比32位寄存器略好。
让我们看看这里是否有更多的流水线操作。这涉及更多,所以我实际测试了一下。每个术语单独总计为64,所有总和为256.
inline int pop4(unsigned long x, unsigned long y, unsigned long u, unsigned long v) { enum { m1 = 0x5555555555555555, m2 = 0x3333333333333333, m3 = 0x0F0F0F0F0F0F0F0F, m4 = 0x000000FF000000FF }; x = x - ((x >> 1) & m1); y = y - ((y >> 1) & m1); u = u - ((u >> 1) & m1); v = v - ((v >> 1) & m1); x = (x & m2) + ((x >> 2) & m2); y = (y & m2) + ((y >> 2) & m2); u = (u & m2) + ((u >> 2) & m2); v = (v & m2) + ((v >> 2) & m2); x = x + y; u = u + v; x = (x & m3) + ((x >> 4) & m3); u = (u & m3) + ((u >> 4) & m3); x = x + u; x = x + (x >> 8); x = x + (x >> 16); x = x & m4; x = x + (x >> 32); return x & 0x000001FF; }
我很兴奋,但事实证明gcc正在使用-O3播放内联技巧,即使我在某些测试中没有使用inline关键字。当我让gcc玩弄技巧时,十亿次调用pop4()需要12.56个gigatcles,但我确定它是将参数折叠为常量表达式。一个更现实的数字似乎是19.6gc,另外30%的加速。我的测试循环现在看起来像这样,确保每个参数足够不同以阻止gcc玩弄技巧。
hitime b4 = rdtsc(); for (unsigned long i = 10L * 1000*1000*1000; i < 11L * 1000*1000*1000; ++i) sum += pop4 (i, i^1, ~i, i|1); hitime e4 = rdtsc();
在8.17s中总计了2560亿比特。在16位表查找中作为基准测试,以3200万位的速度运行到1.02s。无法直接比较,因为另一个工作台没有给出时钟速度,但看起来我已经从64KB表版本中打出了鼻涕,这首先是对L1缓存的悲剧性使用。
更新:决定做明显的事情并通过添加四个重复的行来创建pop6()。得出22.8gc,经过9.5s总计3840亿比特。所以还有另外20%现在800毫秒,320亿比特。
答案 9 :(得分:26)
为什么不迭代地除以2?
count = 0 while n > 0 if (n % 2) == 1 count += 1 n /= 2
我同意这不是最快的,但“最好的”有点含糊不清。我认为“最好的”应该有一个清晰的元素
答案 10 :(得分:23)
当你写出位模式时,Hacker's Delight bit-twiddling变得更加清晰。
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;
}
第一步将偶数位加到奇数位,产生每两位的位数。其他步骤将高阶块添加到低阶块,将块大小加倍,直到我们将最终计数占用整个int。
答案 11 :(得分:20)
对于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;
}
答案 12 :(得分:17)
这可以在O(k)
中完成,其中k
是设置的位数。
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
答案 13 :(得分:17)
这不是最快或最好的解决方案,但我找到了同样的问题,我开始思考和思考。最后我意识到,如果从数学方面得到问题并绘制图形,然后你发现它是一个具有某些周期性部分的函数,然后你意识到周期之间的差异......就这样可以这样做。你走了:
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);
}
}
答案 14 :(得分:10)
您正在寻找的功能通常称为二进制数的“横向总和”或“人口数”。 Knuth在前分册1A,第11-12页中对此进行了讨论(尽管在第2卷,4.6.3-(7)中有一个简短的参考文献。)
locus classicus 是Peter Wegner在Communications of the ACM, Volume 3 (1960) Number 5, page 322中发表的文章“计算二进制计算机中的一种技术”。他在那里给出了两种不同的算法,一种针对期望为“稀疏”的数字进行优化(即,具有少量的数字),并针对相反的情况进行优化。
答案 15 :(得分:9)
几个未解决的问题: -
我们可以修改算法以支持负数,如下所示: -
count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
count += 1
n /= 2
return count
现在要克服第二个问题,我们可以写下算法: -
int bit_count(int num)
{
int count=0;
while(num)
{
num=(num)&(num-1);
count++;
}
return count;
}
有关完整参考,请参阅:
http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html
答案 16 :(得分:9)
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;
}
答案 17 :(得分:8)
我使用下面更直观的代码。
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
逻辑:n&amp; (n-1)重置n的最后一位。
P.S:我知道这不是O(1)解决方案,尽管这是一个有趣的解决方案。答案 18 :(得分:8)
我认为Brian Kernighan's方法也很有用...... 它经历了与设置位一样多的迭代。因此,如果我们有一个只有高位设置的32位字,那么它只会循环一次。
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向我指出,这种方法首先由Peter Wegner在CACM 3(1960),322中发表。(也由Derrick Lehmer独立发现并于1964年在Beckenbach编辑的一本书中发表) 。)&#34;
答案 19 :(得分:7)
如果你正在使用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 ++编译器中工作,如果一个常量值用于位数,它基本上只为你展开你的循环(换句话说,我很确定这是你能找到的最快的通用方法)
答案 20 :(得分:7)
我在1990年左右为RISC机器编写了一个快速的bitcount宏。它不使用高级算术(乘法,除法,%),内存提取(方式太慢),分支(方式太慢),但它确实假设CPU有一个32位桶形移位器(换句话说,>&gt;&gt;&gt;&gt; 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
所以如果我取上面的第一列(A),将它向右移1位,然后从AB中减去它,得到输出(CD)。 3位的扩展是类似的;如果你愿意的话,你可以用我上面的8行布尔表来检查它。
答案 21 :(得分:7)
“最佳算法”是什么意思?短代码或禁食代码?您的代码看起来非常优雅,并且具有恒定的执行时间。代码也很短。
但如果速度是主要因素而不是代码大小,那么我认为跟随可以更快:
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位值可能会更快。
答案 22 :(得分:6)
Java JDK1.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);
}
答案 23 :(得分:6)
我总是在竞争性编程中使用它,它易于编写和高效:
#include <bits/stdc++.h>
using namespace std;
int countOnes(int n) {
bitset<32> b(n);
return b.count();
}
答案 24 :(得分:6)
我在使用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];
}
答案 25 :(得分:6)
我特别喜欢财富档案中的这个例子:
#define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255) #define BX_(x) ((x) - (((x)>>1)&0x77777777) - (((x)>>2)&0x33333333) - (((x)>>3)&0x11111111))
我最喜欢它,因为它太漂亮了!
答案 26 :(得分:5)
有许多算法来计算设定位;但我认为最好的一个是更快的! 您可以在此页面上看到详细信息:
我建议这个:
使用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个操作;第二种选择需要10;第三个选项需要15个。
答案 27 :(得分:5)
这是一个便携式模块(ANSI-C),可以对任何架构上的每个算法进行基准测试。
你的CPU有9位字节?没问题:-)目前它实现了2种算法,K&amp; R算法和字节明智的查找表。查找表平均比K&amp; 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
答案 28 :(得分:5)
快速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
};
}
答案 29 :(得分:4)
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的解决方案更直观,无论它们有多快。这取决于你对“最佳算法”的定义。
答案 30 :(得分:4)
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时,我会尝试一下,带有g++-9 -std=c++2a
的GCC 9.1.0仍然不支持。
该建议说:
标题:
<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
也被添加来进行循环位旋转:Best practices for circular shift (rotate) operations in C++
答案 31 :(得分:3)
你可以做的是
while(n){
n=n&(n-1);
count++;
}
这背后的逻辑是n-1的位从n的最右设置位反转。 如果n = 6,即110 那么5是101,这个位从n的最右设置位反转。 所以,如果我们&amp;这两个我们将在每次迭代中使最右边的位为0,并且总是转到下一个最右边的设置位。因此,计数设置位。当每个位置位时,最差的时间复杂度将为O(logn)。
答案 32 :(得分:2)
简单的解决方案
时间复杂度为 O(n 中的位数)
int countSet(int n)
{
int res=0;
while(n>0){
res=res+(n&1);
n=n>>1;
}
return res;
}
Brian Kerningam 的算法
时间复杂度为 O(n 中没有设置位)
int countSet(int n)
{
int res=0;
while(n>0)
{
n=(n & (n-1));
res++;
}
return res;
}
32 位数字的查找表方法-在这种方法中,我们将 32 位数字分成四个 8 位数字的块
时间复杂度为 O(1)
int table[256]; /*here size of the table is take 256 because when all 8 bit are set
its decimal representation is 256*/
void initialize() //holds the number of set bits from 0 to 255
{
table[0]=0;
for(int i=1;i<256;i++)
table[i]=(i&1)+table[i>>1];
}
int countSet(int n)
{
// 0xff is hexadecimal representation of 8 set bits.
int res=table[n & 0xff];
n=n>>8;
res=res+ table[n & 0xff];
n=n>>8;
res=res+ table[n & 0xff];
n=n>>8;
res=res+ table[n & 0xff];
return res;
}
答案 33 :(得分:2)
我个人用这个:
public static int myBitCount(long L){
int count = 0;
while (L != 0) {
count++;
L ^= L & -L;
}
return count;
}
答案 34 :(得分:1)
从Python 3.10开始,您将可以使用int.bit_count()
函数,但是暂时可以自己定义此函数。
def bit_count(integer):
return bin(integer).count("1")
答案 35 :(得分:1)
这是迄今为止尚未提及的使用位域的解决方案。以下程序使用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表。
答案 36 :(得分:1)
您可以使用名为__builtin_popcount()的内置函数。 C ++中没有_builtin_popcount,但它是GCC编译器的内置函数。此函数以整数形式返回设置位数。
int __builtin_popcount (unsigned int x);
答案 37 :(得分:1)
在Java 8或9中,只需调用Integer.bitCount
。
答案 38 :(得分:1)
另一种汉明加权算法,如果您使用的是支持BMI2的CPU
the_weight=__tzcnt_u64(~_pext_u64(data[i],data[i]));
玩得开心!
答案 39 :(得分:1)
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; }
答案 40 :(得分:1)
int bitcount(unsigned int n)
{
int count=0;
while(n)
{
count += n & 0x1u;
n >>= 1;
}
return count;
}
迭代'计数'的运行时间与总位数成正比。它只是循环遍历所有位,由于while条件而稍微提前终止。有用,如果1'或设置位稀疏且最低有效位。
答案 41 :(得分:0)
提供另一种未提及的算法,称为 Parallel,采用 from here。很好的一点是它是通用的,这意味着代码对于位大小 8、16、32、64、128 是相同的。
我检查了 2^26 个数字大小的 8、16、32、64 位数的值和计时的正确性。请参阅下面的计时。
提供这个算法作为第一个代码片段,这里提到另外两个仅供参考,因为我测试并比较了它们。
算法是用 C++ 编码的,是通用的,但可以很容易地采用旧的 C。
#include <type_traits>
#include <cstdint>
template <typename IntT>
inline size_t PopCntParallel(IntT n) {
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
using T = std::make_unsigned_t<IntT>;
#define MY_CHAR_BIT 8
T v = T(n);
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
return size_t((T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * MY_CHAR_BIT); // count
#undef MY_CHAR_BIT
}
以下是我比较的两种算法。一种是带循环的 Kernighan 简单方法,取自 here。
template <typename IntT>
inline size_t PopCntKernighan(IntT n) {
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
using T = std::make_unsigned_t<IntT>;
T v = T(n);
size_t c;
for (c = 0; v; ++c)
v &= v - 1; // clear the least significant bit set
return c;
}
另一个是使用内置的 __popcnt16()
/__popcnt()
/__popcnt64()
MSVC 的内在 (doc here),这个内在应该提供非常优化的版本,可能是硬件:>
template <typename IntT>
inline size_t PopCntMsvcIntrinsic(IntT n) {
// https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64?view=msvc-160
using T = std::make_unsigned_t<IntT>;
T v = T(n);
if constexpr(sizeof(IntT) <= 2)
return __popcnt16(uint16_t(v));
else if constexpr(sizeof(IntT) <= 4)
return __popcnt(uint32_t(v));
else
return __popcnt64(uint64_t(v));
}
以下是计时,以每一个数字的纳秒为单位。所有计时都是针对 2^26 个随机数完成的。比较了所有 3 种算法和 8、16、32、64 之间的所有位大小的时间。总而言之,所有测试在我的机器上花费了 16 秒。使用了高分辨率时钟。
08 bit MsvcIntrinsic 4.8 ns
08 bit Parallel 4.0 ns
08 bit Kernighan 18.1 ns
16 bit MsvcIntrinsic 0.7 ns
16 bit Parallel 0.7 ns
16 bit Kernighan 25.7 ns
32 bit MsvcIntrinsic 1.6 ns
32 bit Parallel 1.6 ns
32 bit Kernighan 31.2 ns
64 bit MsvcIntrinsic 2.5 ns
64 bit Parallel 2.4 ns
64 bit Kernighan 49.5 ns
正如人们所见,提供的并行算法(3 个中的第一个)与 MSVC 的内在算法一样好,对于 8 位,它甚至更快。
答案 42 :(得分:0)
如何将整数转换为二进制字符串并对其进行计数?
php解决方案:
substr_count( decbin($integer), '1' );
答案 43 :(得分:0)
def hammingWeight(n: int) -> int:
sums = 0
while (n!=0):
sums+=1
n = n &(n-1)
return sums
在二进制表示中,n 中的最低有效 1 位始终对应于 n - 1 中的 0 位。因此,将 n 和 n - 1 两个数字始终翻转 n 中的最低有效 1 位为0,并保持所有其他位相同。
答案 44 :(得分:0)
#!/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
答案 45 :(得分:0)
Kotlin 1.4 之前
fun NumberOfSetBits(i: Int): Int {
var i = i
i -= (i ushr 1 and 0x55555555)
i = (i and 0x33333333) + (i ushr 2 and 0x33333333)
return (i + (i ushr 4) and 0x0F0F0F0F) * 0x01010101 ushr 24
}
这或多或少是此处看到的答案的副本https://stackoverflow.com/a/109025/6499953
使用 java 修复,然后使用 Intellij IDEA Community Edition 中的转换器进行转换
1.4 及更高版本(截至 21 年 5 月 5 日可能会发生变化)
fun NumberOfSetBits(i: Int): Int {
return i.countOneBits()
}
在引擎盖下它使用 Integer.bitCount
,如此处所示
@SinceKotlin("1.4")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
public actual inline fun Int.countOneBits(): Int = Integer.bitCount(this)
答案 46 :(得分:0)
我没有在任何地方看到这种方法:
int nbits(unsigned char v) {
return ((((v - ((v >> 1) & 0x55)) * 0x1010101) & 0x30c00c03) * 0x10040041) >> 0x1c;
}
它按字节工作,因此对于32位整数必须调用4次。它来自侧向加法,但使用两个32位乘法将指令数量减少到只有7个。
当显然请求数量是4的倍数时,大多数当前的C编译器将使用SIMD(SSE2)指令优化此功能,并且它变得非常有竞争力。它是可移植的,可以定义为宏或内联函数,不需要数据表。
这种方法可以扩展为使用64位乘法一次16位工作。但是,当所有16位都置1时,它会失败,返回零,所以只有当0xffff输入值不存在时才能使用它。由于64位操作,它也较慢,并且不能很好地优化。
答案 47 :(得分:0)
一种简单的方法,可以很好地处理少量的比特,就像这样(在本例中为4位):
(i&amp; 1)+(i&amp; 2)/ 2 +(i&amp; 4)/ 4 +(i&amp; 8)/ 8
其他人是否会建议将此作为一个简单的解决方案用于少量位?
答案 48 :(得分:0)
这是在PHP中有效的东西(所有PHP整数都是32位签名,这是31位):
function bits_population($nInteger)
{
$nPop=0;
while($nInteger)
{
$nInteger^=(1<<(floor(1+log($nInteger)/log(2))-1));
$nPop++;
}
return $nPop;
}
答案 49 :(得分:0)
对于Java,有一个java.util.BitSet
。
https://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html
cardinality():返回在此BitSet中设置为true的位数。
由于将BitSet存储为Long,因此BitSet的存储效率很高。
答案 50 :(得分:0)
一种简单的算法来计算设置位数:
int countbits(n){
int count = 0;
while(n != 0){
n = n & (n-1);
count++;
}
return count;
}
以11(1011)为例,尝试手动运行算法。应该会对您有很大帮助!
答案 51 :(得分:0)
以下是示例代码,可能很有用。
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;
}
答案 52 :(得分:0)
我很失望地看到没人响应功能性的主竞赛递归解决方案,这是迄今为止最纯的解决方案(可以使用任何位长!):
template<typename T>
int popcnt(T n)
{
if (n>0)
return n&1 + popcnt(n>>1);
return 0;
}
答案 53 :(得分:0)
def hammingWeight(n):
count = 0
while n:
if n&1:
count += 1
n >>= 1
return count
答案 54 :(得分:-1)
// How about the following:
public int CountBits(int value)
{
int count = 0;
while (value > 0)
{
if (value & 1)
count++;
value <<= 1;
}
return count;
}
答案 55 :(得分:-1)
这是golang中的实现
func CountBitSet(n int) int {
count := 0
for n > 0 {
count += n & 1
n >>= 1
}
return count
}
答案 56 :(得分:-1)
您可以执行以下操作:
int countSetBits(int n)
{
n=((n&0xAAAAAAAA)>>1) + (n&0x55555555);
n=((n&0xCCCCCCCC)>>2) + (n&0x33333333);
n=((n&0xF0F0F0F0)>>4) + (n&0x0F0F0F0F);
n=((n&0xFF00FF00)>>8) + (n&0x00FF00FF);
return n;
}
int main()
{
int n=10;
printf("Number of set bits: %d",countSetBits(n));
return 0;
}
见heer:http://ideone.com/JhwcX
工作可以解释如下:
首先,所有偶数位都向右移位。添加奇数位以计算两个组中的位数。 然后我们分成两组,然后是四组和二组。等...... ..
答案 57 :(得分:-1)
我提供两种算法来回答这个问题,
package countSetBitsInAnInteger;
import java.util.Scanner;
public class UsingLoop {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try{
System.out.println("Enter a integer number to check for set bits in it");
int n = in.nextInt();
System.out.println("Using while loop, we get the number of set bits as: "+usingLoop(n));
System.out.println("Using Brain Kernighan's Algorithm, we get the number of set bits as: "+usingBrainKernighan(n));
System.out.println("Using ");
}
finally{
in.close();
}
}
private static int usingBrainKernighan(int n) {
int count = 0;
while(n>0){
n&=(n-1);
count++;
}
return count;
}/*
Analysis:
Time complexity = O(lgn)
Space complexity = O(1)
*/
private static int usingLoop(int n) {
int count = 0;
for(int i=0;i<32;i++){
if((n&(1<<i))!=0)
count++;
}
return count;
}
/*
Analysis:
Time Complexity = O(32) // Maybe the complexity is O(lgn)
Space Complexity = O(1)
*/
}
答案 58 :(得分:-2)
This program will let you enter characters and combine them into a string.
First tell us how many characters you want to enter.
3
Enter a character!
DOGS
Enter a character!
Enter a character!
Your word is DOG
答案 59 :(得分:-2)
我使用以下功能。没有检查基准,但它的工作原理。
int msb(int num)
{
int m = 0;
for (int i = 16; i > 0; i = i>>1)
{
// debug(i, num, m);
if(num>>i)
{
m += i;
num>>=i;
}
}
return m;
}
答案 60 :(得分:-3)
这也可以正常工作:
int ans = 0;
while(num){
ans += (num &1);
num = num >>1;
}
return ans;