`
BrokenDreams
  • 浏览: 249237 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
68ec41aa-0ce6-3f83-961b-5aa541d59e48
Java并发包源码解析
浏览量:98046
社区版块
存档分类
最新评论

计算一个int型数值中bit-1的个数

 
阅读更多
        之前面试遇到过这样的问题:写一个方法,统计出一个int型数值中比特值为1的比特个数,后来群里也讨论过,在这里总结一下。
        直观的看一下这个问题的解法。
        假设一个int型数值为80。
        首先,将这个数值转化成二进制形式。
80 = 00000000 00000000 00000000 01010000

        然后数一数为1的bit有几个,很明显是2个,那么结果就是2。

        但是写程序要怎么做呢?
        首先看一个小技巧,x & (x-1)会把x中最右边为1的bit变成0,利用这个小技巧我们可以写出下面的程序:
public static int bitCount(int n){
		int count = 0;
		while(n != 0){
			n = n & (n-1);
			count++;
		}
		return count;
	}

        这个过程相当于从右往左数1的个数,假设n为-1,就得数32次,相当于一个线性时间的操作,有没有更好的呢?

        在Hackers Delight里面介绍了很好的技巧。
        这种技巧是一种分治的思想,假设我们要求32位数值的bit-1个数,我们只要计算出前16位和后16位的bit-1个数,然后把他们加起来就行了,那么对于16位的bit-1个数的求法,我们采取同样的方式...直到我们计算2位bit-1个数的时候,我们只要把两个1位bit的值加起来就好了,这相当于是子问题的基本情况,递归的出口。
        下面的图展示了这个过程:

注:图片来之Hackers Delight。

        按照这个思路,我们便可以写出如下代码:
public static int bitCount1(int n){
		n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
		n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
		n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
		n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
		n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);
		return n;
	}

        相对于上面线性时间的求法来说,这个"分治"的求法只用了log(32)=5步便完成了运算,非常巧妙。

        但是。这个还能优化,首先想一下结果最大也就是32了,所以可以减少一些"与"操作,在最后让n和0x0000003f相与就行了。从上图可以看出,当我们在进行8位到16位步骤的时候,8位的高4位对于我们来说已经没用了,所以从这里开始,我们可以不管高位,减少"与"操作,这样能减少一些指令。
        代码如下:
public static int bitCount1(int n){
		n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
		n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
		n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
		n = n + (n >>> 8);
		n = n + (n >>> 16);
		return n & 0x0000003f;
	}

        其实n可以看成是(n & 0x55555555)加上(((n >>> 1) & 0x55555555) << 1),也就是
n = (n & 0x55555555) + (((n >>> 1) & 0x55555555) * 2)
(n & 0x55555555) = n - (((n >>> 1) & 0x55555555) * 2)

        将这个代入上面的程序,就得到如下代码:
public static int bitCount2(int n){
		n = n - ((n >>> 1) & 0x55555555);
		n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
		n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
		n = n + (n >>> 8);
		n = n + (n >>> 16);
		return n & 0x0000003f;
	}

        这样一来,代码的第一行又减少了一个指令。
        OK!
    
        这段代码也被Java采用作为Integer类中bitCount方法的实现。
  • 大小: 44.5 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics