求Integer中二进制1的个数

来源:互联网 发布:openwrt 网络重启命令 编辑:程序博客网 时间:2024/05/22 15:40

尊重原创,转载请标明出处   http://blog.csdn.net/abcdef314159

在上一篇中详细介绍了Android中的Integer类,其中有一个方法bitCount(int i),他表示返回二进制中1的个数 ,我们看一下

public static int countBit(int i) {i = i - ((i >>> 1) & 0x55555555);i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);i = (i + (i >>> 4)) & 0x0f0f0f0f;i = i + (i >>> 8);i = i + (i >>> 16);return i & 0x3f;}

如果不明白可以看一下Integer源码详解,如果不太好理解那么就再改一下。

public static int countBit(int i) {i = (i & 0x55555555) + ((i >> 1) & 0x55555555);i = (i & 0x33333333) + ((i >> 2) & 0x33333333);i = (i & 0x0f0f0f0f) + ((i >> 4) & 0x0f0f0f0f);i = (i & 0x00ff00ff) + ((i >> 8) & 0x00ff00ff);i = (i & 0x0000ffff) + ((i >> 16) & 0x0000ffff);return i & 0x3f;}

这个就比较通俗易懂,更好理解。那么除了bitCount方法以外还有没有其他的方法来求二进制中1的个数呢,那么这一篇就来详细介绍一下求二进制中1的个数的其他方法。

public static int countBit(int i) {int sum = 0;while (i != 0) {if (i % 2 == 1) {sum++;}i /= 2;}return sum;}

这是最简单的方法,乍一看好像还很对,我们看一下
System.out.println(countBit(33));System.out.println(countBit(15));System.out.println(countBit(0));System.out.println(countBit(-1));

运行结果

2400

因为负数是通过补码的形式存在的,-1的补码是32个一,很明显最后一个是错误的。那么在修改一下

public static int countBit(int i) {int sum = 0;while (i != 0) {if (i % 2 == 1) {sum++;}i = (i >>> 1);}return sum;}

既然负数是通过补码形式存在的,那么通过移位是不是就没问题了,再看一下运行结果

24031

可是这样发现也不对,明明是32个1的,这里为什么是31个,其实这里犯了一个大忌,因为判断一个数是否是奇偶数不能用等号,要用不等号,为什么这么说,因为负奇数对2求余是得-1的,这里再改一下,用不等号

public static int countBit(int i) {int sum = 0;while (i != 0) {if (i % 2 != 0) {sum++;}i = (i >>> 1);}return sum;}
再看一下运行结果

24032

所以这种写法才是正确的。当然还可以这样写

public static int countBit(int i) {int sum = 0;while (i != 0) {if ((i&1)!= 0) {sum++;}i = (i >>> 1);}return sum;}

运行结果也是正确的,当然还可以这样写

public static int countBit(int i) {int sum = 0;while (i != 0) {sum+=i&1;i = (i >>> 1);}return sum;}

下面这样写也对

public static int countBit(int i) {int sum = 0;while (i != 0) {i &= i - 1;sum++;}return sum;}
这样可能不太好理解,如果你随便写几个就会明白,他会把最后的1一个个消除,直到变为0为止。还有下面这种方式
public static int countBit(int i) {int count = 0;for (int j = 0; j < 32; j++) {if ((i & (1 << j)) >>> j == 1)count++;}return count;}

原理很简单就是通过1与各位上的数字与运算,如果为1就count++,当然还可以改为下面这种方式

public static int countBit(int i) {int count = 0;for (int j = 0; j < 32; j++) {if ((i & (1 << j)) != 0)count++;}return count;}
判断每个位置上是否有1,还可以下面修改
public static int countBit(int i) {int count = 0;for (int j = 0; j < 32; j++) {if ((i & 1) == 1)count++;i >>= 1;}return count;}

这个使用i>>=1,严格来说应该使用i>>>=1更合适,但为什么前面一种方式也不会差错是因为for循环中就循环了32次,即使是负数也没关系,结果一样正确,下面再看一下其他方式。

public static int countBit(int i) {//table是0到15转化为二进制时1的个数int table[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };int count = 0;while (i != 0) {//通过每4位计算一次,求出包含1的个数count += table[i & 0xf];i >>>= 4;}return count;}
注释很明显就不在详述,上面只是4个一组求的,还可以八个一组,当然数组元素也很多,这里就不在列出。通过发现,只要认真挖掘总还能找到,那就继续,在上一篇讲到bitCount方法的时候,第一步是每两位存储1的个数,那么能不能每3位存储呢,当然可以,看代码
public static int countBit(int i) {// 每3个计算存储int tmp = (i - ((i >>> 1) & 033333333333) - ((i >>> 2) & 011111111111));// 因为Integer是32位,这里最前面是2个一个组合,后面的10个是每3个组合,正好是32位,总共11组,后面的每2组相加,// 最前面的一组不动,所以下面的与运算最前面的3把它保留了(030707070707每3位一组,这里是8进制)tmp = ((tmp + (tmp >>> 3)) & 030707070707);// 每6位数相加,即2组tmp = ((tmp + (tmp >>> 6)) & 07700770077);tmp = ((tmp + (tmp >>> 12)) & 037700007777);// 这里为什么要与63进行与运算,按说如果与077777777进行与运算也没错,8进制的8个7相当于24个1//但因为Integer最多也就32个1,所以取后几位也就足够了return ((tmp + (tmp >>> 24))) & 63;}
这个方法也是可以的,只不过在最开始的时候他是每3个存放,原理很简单,主要把上面的8进制换成二进制就一目了然。那么既然能每3个存储,每四个是不是也可,当然可以,看代码
public static int countBit(int i) {int tmp = i - ((i >>> 1) & 0x77777777) - ((i >>> 2) & 0x33333333)- ((i >>> 3) & 0x11111111);tmp = ((tmp + (tmp >>> 4)) & 0x0f0f0f0f);tmp = ((tmp + (tmp >>> 8)) & 0x00ff00ff);return ((tmp + (tmp >>> 16)) & 0x0000ffff) % 63;}
这个比上面一个更简单,因为4个一组正好分成8组,不会像上面3个一组出现分不均的情况。当然还有其他方法也可以求,这里就不在介绍,也可以参考一下Hamming weight


原创粉丝点击