并行bit count算法

计算一个整型数字中有多少个bit被置1,一般会想到按位循环遍历的方法。

这里有一个并行计算的方法如下:

先上代码

v = v - ((v >> 1) & 0x55555555);                    // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);     // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count

就这3步,只有12个operation,
它采用了分治法思想并行计算,

第一行计算了相邻2位的bit和,相邻2位的意思是32bit的数中把bit两两分组,计算每组的bit和,
可以看到0x555是一个magic number,
0x55555555 = 01010101 01010101 01010101 01010101

第一行代码的效果
首先右移一位,把高位的数给了低位,然后0x55555555是掩码,把高位置0,得到了新的2位数,再用原来的数减去刚刚得到新的两位数,得到想要的结果:

00b >> shifted: ?0b >> masked: 00b >> subtraction: 00b - 00b >> 00b
01b >> shifted: ?0b >> masked: 00b >> subtraction: 01b - 00b >> 01b
10b >> shifted: ?1b >> masked: 01b >> subtraction: 10b - 01b >> 01b
11b >> shifted: ?1b >> masked: 01b >> subtraction: 11b - 01b >> 10b

可以看到得到了相邻2位的bit数。

相邻2位的算出来以后,再进入更大的组,相邻4位,就是4个一组,计算每组的bit数,
而我们看到,4个一组中的2小组都是刚才已经计算好了的。
所以这4个一组中要计算的是里面两个小组的和(刚才相邻两位计算出来的是每两位的bit数,它们相加就是相邻4位的bit数)。

既然是相邻4位,换一个magic number
0x33333333 = 00110011 00110011 00110011 00110011

第2行代码计算的是相邻4位的bit数,效果如下

要计算的是1010b中相邻的两小组10b和10b的和,应该是10b + 10b = 100b
(注意10b是上一行代码算的相邻2位的bit和)

1010b   ---->>-----   0010b
0011b                 0011b
----&----           ----&----
0010b                 0010b
-------------+---------------
           0100b

第3行代码就是进入更大的组,相邻8位的和,思路和上面一样,这回的magic number是
0x0000FFFF = 00000000 00000000 11111111 11111111

第3行(v + (v >> 4) & 0xF0F0F0F)的效果就是计算出相邻8位(8位一组)的bit数

然后32bit的数字可以分为4个8位(A B C D)
最后要求A,B,C,D这4个组的bit数之和,

最后用了bit乘法,* 0x1010101
bit乘法的计算法则如下,和十进制数乘法一样的
在这里插入图片描述
所以最后(A B C D) * 0x1010101的结果是(A+B+C+D, B+C+D, C+D, D),
右移24位就是我们要的A+B+C+D.

参考链接

### 关于 `count_bits()` 函数的实现与用法 `count_bits()` 是一种常见的编程函数,通常用来计算整数中二进制表示下的“1”的个数。这种操作也被称为 **Hamming Weight** 或者 **Population Count**。以下是该函数的一些常见实现方式及其应用。 #### 使用位运算的方法 通过逐位检查的方式可以实现 `count_bits()` 的功能。这种方法的核心思想是对输入数值中的每一位进行检测并累加其值为“1”的次数: ```c int count_bits(int n) { int count = 0; while (n) { count += n & 1; // 检查最低位是否为1 n >>= 1; // 右移一位继续处理下一位 } return count; } ``` 此方法的时间复杂度取决于输入数据的有效比特位长度[^5]。 #### 利用内置指令优化性能 现代处理器提供了专门针对此类问题设计的硬件支持命令,例如 GCC 编译器可以通过内联汇编或者特定扩展来利用这些特性加速执行过程: ```cpp #include <bitset> // C++ STL 提供的标准库解决方案 size_t count_bits(unsigned long long x){ return std::bitset<sizeof(x)*CHAR_BIT>(x).count(); } // 或者借助 __builtin_popcount() unsigned int count_bits(unsigned int x){ return __builtin_popcount(x); } ``` 这里提到的 `__builtin_popcount()` 属于 GNU C Compiler 特有的内置函数之一,它能够高效完成相同任务而无需手动编写循环逻辑[^6]。 #### 并行化考虑因素 当面对大规模数组或者其他集合类型的批量求解场景时,则需引入多线程技术提升整体效率。依据前述资料描述可知,在构建适合并发环境的应用程序框架过程中,应当注重以下几个方面的工作内容[3]: - 创建独立的任务单元负责不同子集范围内的统计工作; - 设计合理的同步机制保障最终汇总阶段的数据一致性; - 合理分配资源避免竞争条件影响吞吐表现; 综上所述,无论是单机版还是分布式版本的设计方案都需要综合考量算法本身特点以及目标平台架构约束等因素的影响效果才能取得最佳实践成果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值