美文网首页探索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