美文网首页探索JDK
探索Integer番外篇之Integer.bitCount(in

探索Integer番外篇之Integer.bitCount(in

作者: 苏小小北 | 来源:发表于2018-10-26 15:12 被阅读0次

1. 前言

钱不是真正的花掉了,而是换了一种方式陪在你身边

bitCount:统计int类型数值的二进制中1的个数。
在各种版本的面试宝典中,这个题目应该是非常常见的了。比如《剑指offer》面试题10:二进制中1的个数,《编程之美》2.1 求二进制数中1的个数,实现方法非常之多,各有千秋。
我们来看看jdk中是如何实现的。

2. 源码


相比较其他的解法,这个是非常新颖的方式,不愧于大师之笔。

    public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x55555555);//第1步
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);//第2步
        i = (i + (i >>> 4)) & 0x0f0f0f0f;//第3步
        i = i + (i >>> 8);//第4步
        i = i + (i >>> 16);//第5步
        return i & 0x3f;//第6步
    }

解析过程:
输入 i,
i = b_0*2^0 + b_1*2^1 + b_2*2^2 + ... + b_{30}*2^{30} + b_{31}*2^{31} \tag{2.1},
其中 b_0,b_1,b_2...b_{31}
要么为0,要么为1,则,统计二进制1的个数,实则求
b_0+b_1+b_2+...+b_{31}
第1步:i = i - ((i >>> 1) & 0x55555555);
i >>> 1的结果
b_1*2^0 + b_2*2^1 + ... + b_{31}*2^{30}\tag{2.2}
而0x55555555中 5 表示 0101,与运算就是取奇数位(取第0位,第2位,第4位...),则
(i>>>1) & 0x55555555的结果为
b_1*2^0 + b_3*2^2 + b_5*2^4 +...+ b_{31}*2^{30} \tag{2.3}
i = i - ((i >>> 1) & 0x55555555)的结果为(2.2+2.3)
\begin{eqnarray*} i &=& (b_0-b_1)*2^0 + b_1*2^1 + (b_2-b_3)*2^2 + ... + (b_{30}-b_{31})*2^{30} + b_{31}*2^{31} \\&=&(b_0+b_1)*2^0 + (b_2+b_3)*2^2+...+(b_{30}+b_{31})*2^{30} \tag{2.4} \end{eqnarray*}
第2步:i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
0x33333333中 表示 0011,与运算就是每四位取前两位
i & 0x33333333的结果为
(b_0+b_1) * 2^0 + (b_4+b_5) * 2^4 + (b_8+b_9) * 2^8 +...+(b_{28}+b_{29})*2^{28} \tag{2.5}
(i>>>2) & 0x33333333的结果为
(b_2+b_3) * 2^0 + (b_6+b_7)*2^4 + (b_10+b_11)*2^{8} +...+(b_{30}+b_{31})*2^{28} \tag{2.6}
i = (i & 0x33333333) + ((i>>>2) & 0x33333333)的结果为(2.5 + 2.6)
\begin{eqnarray*} i &=&(b_0+b_1+b_2+b_3)*2^0 + (b_4+b_5+b_6+b_7)*2^4+...+(b_{28}+b_{29}+b_{30}+b_{31})*2^{28} \tag{2.7} \end{eqnarray*}
第3步:i = (i + (i >>> 4)) & 0x0f0f0f0f;
i + (i>>>4)的结果为
\begin{eqnarray*} (b_0+b_1+b_2+b_3+b_4+b_5+b_6+b_7)*2^0 + (b_4+b_5+b_6+b_7+b_8+b_9+b_{10}+b_{11})*2^4 +\\...+(b_{24}+b_{25}+b_{26}+b_{27}+b_{28}+b_{29}+b_{30}+b_{31})*2^{24}+(b_{28}+b_{29}+b_{30}+b_{31})*2^{28} \end{eqnarray*}
而0x0f0f0f0f中,与运算表示每8位取前四位,
则i = (i + (i>>>4)) & 0x0f0f0f0f的结果为
\begin{eqnarray*} i=(b_0+b_1+b_2+b_3+b_4+b_5+b_6+b_7)*2^0 + (b_8+b_9+b_{10}+b_{11} +b_{12}+b_{13}+b_{14}+b_{15})*2^8 +\\...+(b_{24}+b_{25}+b_{26}+b_{27}+b_{28}+b_{29}+b_{30}+b_{31})*2^{24} \end{eqnarray*}
第4步:i = i + (i >>> 8);
i = i + (i>>>8)的结果为
\begin{eqnarray*} i=&(&b_0+b_1+b_2+...+b_{14}+b_{15})*2^0 +(b_8+b_9+b_{10}+...+b_{22}+b_{23})*2^8+ \\&(&b_{24}+b_{25}+b_{26}+...+b_{30}+b_{31})*2^{16} +(b_{24}+b_{25}+b_{26}+...+b_{30}+b_{31})*2^{24} \end{eqnarray*}
第5步:i = i + (i >>> 16);
i = i + (i>>>16)的结果为
\begin{eqnarray*} i=&(&b_0+b_1+b_2+...+b_{30}+b_{31})*2^0 +(b_8+b_9+b_{10}+...+b_{30}+b_{31})*2^8+ \\&(&b_{24}+b_{25}+b_{26}+...+b_{30}+b_{31})*2^{16} +(b_{24}+b_{25}+b_{26}+...+b_{30}+b_{31})*2^{24} \end{eqnarray*}
第5步:i & 0x3f
0x3f表示只取二进制前6位
结果为:
\begin{eqnarray*} i&=&(b_0+b_1+b_2+...+b_{30}+b_{31})*2^0 \\&=&b_0+b_1+b_2+...+b_{30}+b_{31} \end{eqnarray*}
即为二进制1的个数

3.后言

6步法求二进制1的个数,你学会了吗?


其他

本人也是在慢慢学习中,如有错误还请原谅、敬请指出,谢谢!

相关文章

网友评论

    本文标题:探索Integer番外篇之Integer.bitCount(in

    本文链接:https://www.haomeiwen.com/subject/lutktqtx.html