比特位计数(bit counting)是指对一个二进制数中比特位为1的比特个数进行计数。比特位计数是一个常见操作,常用于数据处理、通讯数据校验、图像处理、编码和解码等领域。
利用位操作技巧来进行比特位计数。
移位算法
一种最简单的常用算法是移位法:使用“按位与”(&)运算和“移位”运算,“按位右移”(>>)或“按位左移”(<<)来逐位遍历统计。
“按位右移”(>>)的比特位计数版本:
/***比特位计数的右移位法版本***/
public static int bitCountShiftR (int n)
{
int count=0;
while (n!=0)
{ //这里的0X1,也可用01,0B1或1。
count += n & 0B1 ; //0X1
n >>= 1 ;
}
return count ;
}
“按位左移”(<<)的比特位计数版本:
/***比特位计数的左移位法版本,效率不如右移位法***/
public static int bitCountShiftL (int n)
{ //整型数是4字节32个bit
int count=0;
for(int i=0,flag=1; i<32; i++,flag<<=1)
count += (n&flag)/flag;
return count ;
}
另一种简单的算法,是查表法,即借助映射表(键/值)对,把所有的值和结果都一一罗列放在映射表(键/值)中,计算时按关键字查询结果。感觉这种方法有点笨拙。
Brian Kernighan算法实现的比特位计数
下面介绍利用Brian Kernighan算法进行比特位计数的算法。此算法效率比较高。
用 Brian Kernighan算法实现的比特位计数版本如下:
/***比特位计数的 Brian Kernighan算法版本***/
public static int bitCountHmingWeight (int x)
{
int count=0 ;
while (x!=0)
{
x &= (x - 1) ;
count++ ;
}
return count ;
}
Brian Kernighan算法,其实现原理是,每循环一次处理掉二进制编码中最右边一个比特为1的比特位,直至最后所有比特位都为0。
上图是x = 136的二进制编码(10001000),Brian Kernighan算法第一次循环迭代的结果示意图,x = x&(x-1) = 128,二进制编码中只剩下一个比特位为1。
基于以上思路,通过 2 次迭代就可以知道 x = 136(10001000)二进制编码中 1 的位数,而不需要 8 次。
汉明重量
汉明重量是一个信息的二进制编码相对于同样粒度的零的二进制编码的汉明距离。汉明距离是数据传输差错控制中用到的一个概念,用来表示两个编码信息的距离,它表示两个相同粒度的信息的二进制编码中不同比特位的个数。
汉明重量,通俗地说,是一个信息的二进制编码中比特位为 1 的比特位统计数。所以二进制编码10110101 的汉明重量是5。
计算“汉明重量”实际上就是进行比特位计数。
汉明距离是数据传输差错控制中用到的一个概念,用来表示两个编码信息的距离,它表示两个相同粒度的信息的二进制编码中不同比特位的个数。
汉明距离是以理查德·卫斯里·汉明的名字命名的。对两个整型值x、y进行异或运算,并统计比特位1的个数,那么这个统计值就是汉明距离。
对于两个相同粒度的信息的二进制编码,它们的汉明距离等于它们异或后的汉明重量。
//计算两个整型数的汉明距离
public static int hammingDistance(int x, int y)
{
int n = x ^ y;
return bitCountHmingWeight (n); //计算汉明重量
}
在信息论中,两个同样粒度的信息之间的汉明距离,实际上就是从一个信息变换为另一个信息所需要替换的比特位个数。例如:10110101 与 10010101 之间的汉明距离是1,只需变换1个比特位就可转换。
下面是一个完整的比特位计数测试演示程序:
/***比特位计数(bit counting)算法***/
/***
* @author QiuGen
* @description 比特位计数算法BitsCount
* 实现功能:演示一些简单的比特位计数算法
* @date 2024/5/12
* ***/
public class BitsCount {
/***比特位计数的右移位法算法版本***/
public static int bitCountShiftR (int n)
{
int count=0;
while (n!=0)
{ //这里的0X1,也可用01,0B1或1。
count += n & 0B1 ; //0X1
n >>= 1 ;
}
return count ;
}
/***比特位计数的左移位法算法版本,效率不如右移位法***/
public static int bitCountShiftL (int n)
{ //整型数是4字节32个bit
int count=0;
for(int i=0,flag=1; i<32; i++,flag<<=1)
count += (n&flag)/flag;
return count ;
}
/***比特位计数的汉明重量法版本***/
public static int bitCountHmingWeight (int n)
{
int count=0 ;
while (n!=0)
{
n &= (n - 1) ;
count++ ;
}
return count ;
}
//计算两个整型数的汉明距离
public static int hammingDistance(int x, int y)
{
int n = x ^ y;
return bitCountHmingWeight (n);
}
public static void printX(int x) {
System.out.println("x="+x + " bitCountShiftR比特位计算值:"+bitCountShiftR(x));
System.out.println("x="+x + " bitCountShiftL比特位计算值:"+bitCountShiftL(x));
System.out.println("x="+x + " bitCountHmingWeight比特位计算值:"+bitCountHmingWeight(x));
}
public static void main(String[] args) {
int x = 0B01111010010101010010000111110010;
printX(x);
x = 13;
printX(x);
x = 39;
printX(x);
x = 377;
printX(x);
x = hammingDistance(8, 7);
System.out.println("HammingDistance x="+x);
}
}