苏君君出了一道题,是牛客网上面的:

输入一个int型整数,输出该数二进制表示中1的个数。其中负数用补码表示。

其实这道题并不难,大家很容易想到的解法是转成字符串的思路,即如下所示:

    public static int NumberOf1(int n) {
String s = Integer.toBinaryString(n);
int count = 0;
for (int i = 0; i < s.length(); i++) {
if(s.charAt(i) == '1'){
count++;
}
}
return count;
}

但是这样运算速度不快。经过魏印福的点拨,知道了Redis中采用的variable-precision SWAR算法,这个算法非常厉害,因为他没有按照常规思路,而是采用了分组移位的思路。

参见博客:https://blog.csdn.net/jasonbaoly/article/details/47336899

算法实现:

int SWAR(unsigned int i)
{
i = i - ((i >> ) & 0x55555555);
i = (i & 0x33333333) + ((i >> ) & 0x33333333);
return (((i + (i >> )) & 0x0F0F0F0F) * 0x01010101) >> ;
}

下面逐行解释一下:

第一行

i = i - ((i >> ) & 0x55555555);

0x55555555二进制的标识方式如下:

0x55555555 = 0b01010101010101010101010101010101

可以看到的规律是,奇数位为1,偶数位为0。
表达式((i >> 1) & 0x55555555),将i右移一位,并将所有偶数位设置为0.(等效的,我们也可以通过& 0xAAAAAAAA将所有奇数位设置成0,然后再将结果右移1位)为了方便起见,我们将这个中间值命名为j。
当我们将中间值j从原始值i中减去会发生什么?那让我们来看看如果i只有两位是什么情况。

    i           j         i - j
----------------------------------
= 0b00 = 0b00 = 0b00
= 0b01 = 0b00 = 0b01
= 0b10 = 0b01 = 0b01
= 0b11 = 0b01 = 0b10

最后的结论就是i-j的十进制结果就是位数组中1出现的次数。
那么如果i不只是两位数组呢?实际上,很容易发现i-j的最低两位仍然如上表所示,三四位,五六位也是一个道理,等等。需要注意的是:

  • 由于& 0x55555555的巧妙用法,尽管>> 1,i-j的最低两位不会被i的第三位或者更高的位影响。
  • 由于j的最低两位永远不可能在比i的最低两位大。这个减法永远不会向i的第三位借位,因此:对于i-j来说,i的最低两位不会影响i的第三位或者更高位。

实际上这一行就是将32位数组分为16个两位为单位的组,每组分别计算1出现的次数。

第二行:

i = (i & 0x33333333) + ((i >> ) & 0x33333333);

与第一行对比,这一行非常的简单。首先,来看一下0x33333333的二进制表示:

0x33333333 = 0b00110011001100110011001100110011

i & 0x33333333的目的是以4位为分组取四位中的后两位。而(i >> 2) & 0x33333333在把i右移两位后做同样的工作。然后把它们结果加起来。
因此,实际上,这行做的工作就是将最低的两位1出现的次数和最低三四位的1出现的次数相加,得到最低四位的1出现的次数。同样的对于输入的8个四位分组(=16进制数)都是一样的。

第三行:

return (((i + (i >> )) & 0x0F0F0F0F) * 0x01010101) >> ;

(i + (i >> 4)) & 0x0F0F0F0F除了这次是用临近的4位1出现的次数系相加,得到8位为一组的1出现的次数,以外原理跟前一行一样。(和上一行有所不同的是,我们可以把&去掉,因为我们知道原始输入8位不可能出现超过8个1因此二进制值不会超过4位。)
现在我们有一个三十二位数,由四个字节组成,每个字节保存着原始输入中为1的位的数量。(我们把它们称作A,B,C和D。)那么为什么我们用0x01010101乘以这个值(命名为k)?

由于:

0x01010101 = ( << ) + ( << ) + ( << ) + 

可得:

k * 0x01010101 = (k << ) + (k << ) + (k << ) + k

因此,最高位总和就是最终的结果,如下图所示:

k * 0x01010101最高位就是原始输入的1出现次数的最终结果。>> 24只是简单的将最高位的值移到最低位。

另一种更好理解的写法:

int SWAR(unsigned int i)
{
x = (x & 0x55555555) + ((x >> ) & 0x55555555);
x = (x & 0x33333333) + ((x >> ) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> ) & 0x0f0f0f0f);
i = (i*(0x01010101) >> )
return i;
}

[算法]从一道题引出variable-precision SWAR算法的更多相关文章

  1. variable precision SWAR算法

    计算二进制形式中1的数量这种问题,在各种刷题网站上比较常见,以往都是选择最笨的遍历方法“蒙混”过关.在了解Redis的过程中接触到了variable precision SWAR算法(以下简称VP-S ...

  2. variable-precision SWAR算法介绍

    BITCOUNT命令是统计一个位数组中非0进制位的数量,数学上称作:”Hanmming Weight“ 目前效率最好的为variable-precision SWAR算法,可以常数时间内计算出多个字节 ...

  3. variable-precision SWAR算法:计算Hamming Weight

    variable-precision SWAR算法:计算Hamming Weight 转自我的Github 最近看书看到了一个计算Hamming Weight的算法,觉得挺巧妙的,纪录一下. Hamm ...

  4. 【智能算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细解析和TSP代码实例以及01背包代码实例

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 00 目录 局部搜索再次科普 变邻域搜索 造轮子写代码 01 局部搜索科普三连 虽然之前做的很多篇启发式的算法都有跟大家提过局部 ...

  5. EM算法浅析(一)-问题引出

    EM算法浅析,我准备写一个系列的文章: EM算法浅析(一)-问题引出 EM算法浅析(二)-算法初探 一.基本认识 EM(Expectation Maximization Algorithm)算法即期望 ...

  6. 【算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细一看就懂的解析

    更多精彩尽在微信公众号[程序猿声] 变邻域搜索算法(Variable Neighborhood Search,VNS)一看就懂的解析 00 目录 局部搜索再次科普 变邻域搜索 造轮子写代码 01 局部 ...

  7. 从K近邻算法谈到KD树、SIFT+BBF算法

    转自 http://blog.csdn.net/v_july_v/article/details/8203674 ,感谢july的辛勤劳动 前言 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章 ...

  8. 国密SM4对称算法实现说明(原SMS4无线局域网算法标准)

    国密SM4对称算法实现说明(原SMS4无线局域网算法标准) SM4分组密码算法,原名SMS4,国家密码管理局于2012年3月21日发布:http://www.oscca.gov.cn/News/201 ...

  9. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

随机推荐

  1. [转]用GSON 五招之内搞定任何JSON数组

    关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 <Google Gson的使用方法,实现Json结构的相互转换> ,写的很好,通俗易懂. 我为什么写这篇文章呢?因为前几晚跟 ...

  2. python 动态属性

    先来看一下一个常见的Python类的定义: class Person(object): name = None age = None def __init__(self,name,age): self ...

  3. Xilinx 常用模块汇总(verilog)【01】

    作者:桂. 时间:2018-05-07  19:11:23 链接:http://www.cnblogs.com/xingshansi/p/9004492.html 前言 该文私用,不定期更新,主要汇总 ...

  4. .NET CORE2.0发布后没有 VIEWS视图页面文件

    以前做的CORE1.0的项目,发布的时候有views文件夹的,升级VS后用CORE2.0做项目,发布后没有views文件夹了,全编译到一个类似于Niunan.ZYYCY.Web.Precompiled ...

  5. 使用tar命令解压的时候报错not in gzip format

    使用tar命令解压一个xx.tar.gz压缩包的时候报错not in gzip format,后用file xx.tar.gz查看一下格式发现是html document text格式的...

  6. lua--clone

    clone 深度克隆一个值. 格式: value = clone(值) 用法示例: -- 下面的代码,t2 是 t1 的引用,修改 t2 的属性时,t1 的内容也会发生变化 , b = } local ...

  7. 解决myeclipse/eclipse创建或导入maven工程时引发的问题

    起因: 最近学习maven,按照教程把命令行创建的maven工程导入到eclipse/myeclipse,由于库中没有一些依赖包,所以在导入工程的时候开发工具自动下载依赖包.可是,由于天朝特殊环境的问 ...

  8. Java中使用Oracle的客户端 load data和sqlldr命令执行数据导入到数据库中

    Windows环境下测试代码: import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundExcep ...

  9. iOS开发之按钮的基本使用

    实现功能: 点击向上的箭头,按钮图片向上,移动,点击向下的箭头,按钮图片向下移动 点击向左的箭头,按钮图片向左移动,点击向右的箭头,按钮图片向右移动, 点击加号图片放大,点击减号,图片缩小 第一步: ...

  10. js模板引擎art-template使用方法

    art-template是款性能卓越的 js 模板引擎 https://aui.github.io/art-template/ 特性 拥有接近 JavaScript 渲染极限的的性能 调试友好:语法. ...