统计一个位数组中非0二进制位的数量

背景

在平时的工作当中,会遇到很多需要使用bitmap的情况,那么,针对这种状况,统计二进制位中值为1的位一共有多少个的时候,我们需要慎重的根据当前的使用场景来选择自己的算法,比如需要考虑当前的时间复杂度、空间复杂度等等。

问题

统计二进制位中值为1的位的总数

算法

1.遍历法

使用循环按位统计1的个数,其时间复杂度为O(n),空间复杂度为O(1)。

2.查表法

利用一个数组或哈希生成一张表,存储不同二进制编码对应的值为1的二进制位数,那么在使用时,只需要去进行查询,即可在O(1)的时间复杂度内得到结果。

但是,此算法有个弊端,由于算法是采用空间换取时间的方法,当一个二进制数的位长超过一定限度时,对应的表也就会占据很大的空间,也就是说节约时间越多,花费的存储越多。另外此方法还会收到CPU缓存的限制,如果表太大,表在缓存的上下文切换也就越多,可能会导致性能没有想象中那么高。

所以,为了解决此问题,一般情况下,采用适当的二进制位长度来建表,比如8位、16位,这样情况下,可以对上述问题得到一个平衡,不仅可以享受到优越的性能,而且时间开销也没有遍历法高。

当二进制总长度为n,建表的二进制长度为m时,总的时间复杂度为 O ( ⌊ m n ⌋ ) O(\lfloor \frac{m}{n} \rfloor) O(nm)

3.variable-precision SWAR算法

在数学上,我们一般称上述问题为“计算汉明重量”,而当前一直效率最好的通用算法为variable-precision SWAR算法,该算法不仅在常数时间计算多个字节的汉明重量,而且不需要使用任何额外的内存。

//注:此处函数的参数必须为无符号数。如果是有符号数,可以先强转为无符号,再使用此算法。

//计算32位长度位数组的算法实现
uint32_t swar(uint32_t i){
	//相邻两位一组,就是各组的汉明重量
	i = ( i & 0x55555555 ) + ( ( i >> 1 ) & 0x55555555 );

	//相邻四位一组,就是各组的汉明重量
	i = ( i & 0x33333333 ) + ( ( i >> 2 ) & 0x33333333 );

	//相邻八位一组,就是各组的汉明重量
	i = ( i & 0x0F0F0F0F ) + ( ( i >> 4 ) & 0x0F0F0F0F );

	//将结果保存带低八位
	i = ( ( i * 0x01010101 ) >> 24 );
	
	return i;
}

对应算法的详细过程及数学原理,可参考《variable-precision SWAR算法“计算汉明重量”》《variable-precision SWAR算法:计算Hamming Weight》这两篇文章。

Redis中的使用

在Redis中,使用了上述后两种算法来实现BITCOUNT命令。

  • 当处理的二进制位数大于128位,采用variable-precision SWAR算法来计算;
  • 当处理的二进制位数小于128位,采用查表算法来计算;

注:上述数值参考《Redis设计与实现》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值