位计数就是对一个数中具有某些特征的位进行计数。看下面实现: /* bitscount.c:位计数 */ /* 计算x中1位的数目:方案1,采用分治策略 */ inline int pop(unsigned x){ /* 对每个2位字段,先析出其右端的1位,再析出其左端的1位,然后让这两个位相加 */ x=(x & 0x55555555)+((x>>1) & 0x55555555); /* 对每个4位字段,先析出其右端2位字段,再析出其左端2位字段,然后让这两个2位字段相加 */ x=(x & 0x33333333)+((x>>2) & 0x33333333); /* 对每个8位字段,先析出其右端4位字段,再析出其左端4位字段,然后让这两个4位字段相加 */ x=(x & 0x0f0f0f0f)+((x>>4) & 0x0f0f0f0f); /* 对每个16位字段,先析出其右端8位字段,再析出其左端8位字段,然后让这两个8位字段相加 */ x=(x & 0x00ff00ff)+((x>>8) & 0x00ff00ff); /* 最后让两个16位字段相加 */ x=(x & 0x0000ffff)+(x>>16); return x; } /* 方案2:用公式sum_digits(z)=z-[z/2]-[z/4]-...-[z/2**(n-1)], 并结合分治策略来计算 */ inline int pop_2(unsigned x){ unsigned n; n=(x>>1) & 0x77777777; /* 对每个4位字段z,计算[z/2] */ x=x-n; /* 计算z-[z/2] */ n=(n>>1) & 0x77777777; /* 计算[z/4] */ x=x-n; /* 计算z-[z/2]-[z/4] */ x=(x+(x>>4)) & 0x0f0f0f0f; /* 对每相邻的两个4位字段相加,扩展成4个8位字段 */ x=x*0x01010101; /* 乘以0x01010101,即把这4个字节相加, 结果存放在最高阶的那个字节中 */ return x>>24; } /* 方案3:通过x&(x-1)不断地把最右侧的1位改成0位,并记录下这样的修改次数,直到x变成0 */ inline int pop_3(unsigned x){ int n=0; while(x!=0){ ++n; x=x & (x-1); } return n; } /* 计算整个数组中的1位数目 */ int pop_array(unsigned A[], int n){ register int i,j,lim; register unsigned s,s8,x; s=0; for(i=0;i<n;i=i+31){ lim=n <= i+31 ? n : i+31; s8=0; for(j=i;j<lim;++j){ x=A[j]; x=x-((x>>1) & 0x55555555); /* 变成16个2位字段 */ x=(x & 0x33333333)+((x>>2) & 0x33333333); /* 变成8个4位字段 */ x=(x+(x>>4)) & 0x0f0f0f0f; /* 变成4个8位字段 */ s8=s8+x; /* 把这些字累加,直到最大值小于255 */ } x=(s8 & 0x00ff00ff)+((s8>>8) & 0x00ff00ff); /* 变成2个16位字段 */ x=(x & 0x0000ffff)+(x>>16); /* 变成1们32位的字 */ s=s+x; } return s; } /* 位串的奇偶性:判断串的1位是奇数个还是偶数个,可通过计算pop(x)来获得, 也可用下面的快速算法,奇数个1位返回1,偶数个1位返回0 */ int odd_ones(unsigned x){ x=x^(x>>1); x=x^(x>>2); x=x^(x>>4); x=x^(x>>8); x=x^(x>>16); return x & 0x00000001; } /* 计算前导0的个数 */ int nlz(unsigned x){ int n; if(x==0) return (32); n=1; /* 用分治策略 */ if((x>>16)==0){ /* x<=0x0000ffff时 */ n=n+16; x=x<<16; /* 移除前导的16个0 */ } if((x>>24)==0){ /* x<=0x00ffffff时 */ n=n+8; x=x<<8; /* 移除前导的8个0 */ } if((x>>28)==0){ /* x<=0x0fffffff时 */ n=n+4; x=x<<4; /* 移除前导的4个0 */ } if((x>>30)==0){ /* x<=0x3fffffff时 */ n=n+2; x=x<<2; /* 移除前导的2个0 */ } return n-(x>>31); /* 注意n初始时为1,如果移除前导0后的x最高位为0,则n恰好计算了这个0; 如果最高位为1,则没有额外的前导0,n必须要减去多出的1 */ } /* 计算后缀0的个数 */ int ntz(unsigned x){ int n; if(x==0) return (32); n=1; /* 用分治策略:算法的思想类似于nlz中的思想 */ if((x & 0x0000ffff)==0){ n=n+16; x=x>>16; } if((x & 0x000000ff)==0){ n=n+8; x=x<<8; } if((x & 0x0000000f)==0){ n=n+4; x=x>>4; } if((x & 0x00000003)==0){ n=n+2; x=x>>2; } return n-(x &1); } /* 方案2: */ int ntz_2(unsigned x){ /* 用~x&(x-1)析出x的后缀0,因此x后缀0的个数为~x&(x-1)后缀1的个数, 而其前缀均为0,因此ntz(x)=32-nlz(~x&(x-1)) */ return 32-nlz(~x&(x-1)); } /* 方案3: */ int ntz_3(unsigned x){ int n; /* 用~x&(x-1)析出x的后缀0,因此x的后缀0个数为~x&(x-1)的后缀1个数 */ x=~x&(x-1); n=0; while(x!=0){ /* 对1的个数进行计数 */ n=n+1; x=x>>1; } return n; }