//高效程序的奥秘hacker's delight Henry S. Warren, Jr. 著冯速译
//第5 章位计数5.1 1 位计数
// 应用:
// Hamming 距离是矢量间取不同值的对应位的数目,即dist(x, y) = pop(x ^ y);
// 稀疏数组A 压缩后的简便访问方式,可以利用位串数组BITS 记录相应下标有定义的元素,
// 如每个有定义的A[i],BITS 的第I 个元素是1 位。
#include "iostream"
#include "bitset"
#include "limits"
#include "time.h"
using namespace std;
#define out2(T) cout<<bitset<numeric_limits<unsigned int>::digits>(T)<<endl;
#define SIZE(T) (numeric_limits<unsigned T>::digits)
bool SHR31(int x) //逻辑右移位
{
return (unsigned int)x>>31;
}
#define HOUT(x,y,z) hex_out(x,y,z)
inline void hex(int x)
{
int w = cout.width();
cout << "0x";
cout.width(8); cout.fill('0');
cout << hex << x << " ";
cout.width(w);
}
inline void hex(unsigned __int64 x)
{
int w = cout.width();
cout << "0x";
cout.width(16); cout.fill('0');
printf("%I64x ",x);
cout.width(w);
}
void hex_out(int x, int y, int z)
{
hex(x); hex(y); hex(z); cout << endl;
}
// 循环左移位
int rotate(unsigned int x, int n)
{
return (x<<n) | (x >> (32-n));
}
// 统计x 中位1 的个数
// 首先设置每个2 位字段为原来的两个单位的和,然后,求相邻位字段的和,把结果放入相应的位字段,以此类推。
int pop(unsigned int x)
{
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
x = (x & 0x0000FFFF) + ((x >>16) & 0x0000FFFF);
return x;
}
// 对x 的第一个赋值基于下面这个相当巧妙的公式的前两项
// pop(x) = x - floor(x/2) - floor(x/4) - ... - floor(x/2^31);
// 设字是b3b2b1b0
// bi = floor(x/2^i) - 2*floor(x/2^(i+1))
int pop2(unsigned int x)
{
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x = x + (x >> 8);
x = x + (x >>16);
return x & 0x00000003F;
}
// [HAK, item169]
// http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html
// 在DEC PDP-10计算机上只需要10 个指令
// 它是十进制计数的计算机,mod 63 很方便
// 注意:以0 开头的数是八进制数
// 前三项萃取位字段的数目
// 举例:如每三位字段全 111B
// (1): 取出高二位对应1 的值, 有两个1 ,则取出后n 为011
// (3): 取出最高位对应1 的值,有一个1 ,则取出后n 为001
// (2),(4): x - n - n , 刚好11X - 011 - 001 为2+X =高二位1 的数目+X
// 而最低位X 为1 则自然加1,为0 自然加0
// (5): 合并相邻的两个三位字段成六位字段
// (6): mod 63 是怎么操作的呢?
// 假设六位字段值为a6 a5 a4 a3 a2 a1 a0
// 十进制和为a6*64^5 + a5*64^4 + ... + a0
// = a6* (63+1)^5 + a5* (63+1)^4 + ... + a0
// mod 63 刚好剩下a6 + a5 + ... + a0
// 从而把所有六位字段相加,统计1 的总和
int pop3(unsigned int x)
{
#define modu(x, y) (x % 63)
unsigned int n;
n = (x >> 1) & 033333333333;
x = x - n;
n = (n >> 1) & 033333333333;
x = x - n;
x = (x + (x >> 3)) & 030707070707;
x = modu(x, 63);
return x;
}
int pop4(unsigned int x)
{
unsigned int n;
n = (x >> 1) & 0x77777777;
x = x - n;
n = (n >> 1) & 0x77777777;
x = x - n;
n = (n >> 1) & 0x77777777;
x = x - n;
x = (x + (x >> 4)) & 0x0F0F0F0F;
x = x * 0x01010101;
return x >> 24;
}
int pop5(unsigned int x)
{
int n;
n = 0;
while (x != 0) {
n += 1;
x = x & (x - 1); // 把最右侧的1 位改成0 位
}
return n;
}
int pop6(unsigned int x) {
int i, sum;
sum = x;
for (i = 1; i <= 31; i++) {
x = rotate(x, 1);
sum += x;
}
return -sum;
}
// 只计算七位量
int pop7(unsigned int x) {
x = x * 0x02040810;
x = x & 0x11111111;
x = x * 0x11111111;
x = x >> 28;
return x;
}
// 只计算八位量
int pop8(unsigned int x) {
x = x * 0x08040201;
x = x >> 3;
x = x & 0x11111111;
x = x * 0x11111111;
x = x >> 28;
return x;
}
int pop9(unsigned int x) {
int sum;
sum = x;
while (x != 0) {
x = x >> 1;
sum = sum - x;
}
return sum;
}
// table 存放0 到255 的x 的所有pop(x) 值
int pop10(unsigned int x) {
static char table[256] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};
return table[x & 0xFF] +
table[(x >> 8) & 0xFF] +
table[(x >>16) & 0xFF] +
table[(x >>24)];
}
//GNU c 的无符号long long 型
/*
int pop64(unsigned int x) {
unsigned long long y;
y = x * 0x0002000400080010ULL;
y = y & 0x1111111111111111ULL;
y = y * 0x1111111111111111ULL;
y = y >> 60;
return y;
}
*/
// 15 位量算术版本
const unsigned __int64 a = 0x0002000400080010;
const unsigned __int64 b = 0x1111111111111111;
int pop15(short x) {
unsigned __int64 y;
y = x * a;
y = y & b;
y = y * b;
y = y >> 60;
return y;
}
// 生成一个包含4 个8 位部分和的字。然后,尽可能把这些字加起来后,生成一个全字和。
// 相加不会产生溢出的8 位字段的字的数目是floor(255/8) = 31
int pop_array(unsigned int A[], int n) {
int i, j, lim;
unsigned int s, s8, x;
s = 0;
for (i = 0; i < n; i += 31) {
lim = __min(n, i + 31);
s8 = 0;
for (j = i; j < lim; j++) {
x = A[j];
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
s8 = s8 + x;
}
x = (s8 & 0x00FF00FF) + ((s8 >> 8) & 0x00FF00FF);
x = (x & 0x0000FFFF) + (x >> 16);
s = s + x;
}
return s;
}
void test5();
void main()
{
test5();
}
void test5()
{
srand((unsigned)time(NULL));
unsigned int a[256] = {0}; int i;
for (i = 0; i < 256; i++) {a[i] = i;}
cout << pop_array(a, 256) << endl;
for (i = 0; i < 256; i++) {a[i] = 1;}
cout << pop_array(a, 256) << endl;
for (i = 0; i < 256; i++) {a[i] = 0;}
cout << pop_array(a, 256) << endl;
for (i = 0; i < 256; i++) {a[i] = -1;}
cout << pop_array(a, 256) << endl;
for (i = 0; i < 256; i++) {a[i] = rand()%256;}
cout << pop_array(a, 256) << endl;
}
void test4()
{
short x;
x = 0x7FFF; hex(x); cout<< dec << pop15(x) << endl;
x = 0x0001; hex(x); cout<< dec << pop15(x) << endl;
x = 0x4000; hex(x); cout<< dec << pop15(x) << endl;
x = 0x5555; hex(x); cout<< dec << pop15(x) << endl;
x = 0x2AAA; hex(x); cout<< dec << pop15(x) << endl;
x = 0x3333; hex(x); cout<< dec << pop15(x) << endl;
x = 0x2CCC; hex(x); cout<< dec << pop15(x) << endl;
x = 0x7FFF; hex(x); cout<< dec << pop15(x) << endl;
x = 0x5B93; hex(x); cout<< dec << pop15(x) << endl;
x = 0x2C64; hex(x); cout<< dec << pop15(x) << endl;
x = 0x8000; hex(x); cout<< dec << pop15(x) << endl;
}
void test3()
{
unsigned char x;
x = 0x7F; hex(x); cout<< dec << pop8(x) << endl;
x = 0xFF; hex(x); cout<< dec << pop8(x) << endl;
x = 0x0; hex(x); cout<< dec << pop8(x) << endl;
x = 0xAC; hex(x); cout<< dec << pop8(x) << endl;
x = 0x80; hex(x); cout<< dec << pop8(x) << endl;
x = 0xBD; hex(x); cout<< dec << pop8(x) << endl;
x = 0x55; hex(x); cout<< dec << pop8(x) << endl;
x = 0xAA; hex(x); cout<< dec << pop8(x) << endl;
x = 0x0F; hex(x); cout<< dec << pop8(x) << endl;
x = 0xF0; hex(x); cout<< dec << pop8(x) << endl;
x = 0xE4; hex(x); cout<< dec << pop8(x) << endl;
x = 0x110; hex(0x110); cout << dec << pop7(x) << endl;
}
void test2()
{
char x;
x= 0x7F; hex(x); cout << dec << pop7(x) << endl;
x= 0x0; hex(x); cout << dec << pop7(x) << endl;
x= 0x0F; hex(x); cout << dec << pop7(x) << endl;
x= 0x70; hex(x); cout << dec << pop7(x) << endl;
x= 0x55; hex(x); cout << dec << pop7(x) << endl;
x= 0x2A; hex(x); cout << dec << pop7(x) << endl;
x= 0x5D; hex(x); cout << dec << pop7(x) << endl;
x= 0x33; hex(x); cout << dec << pop7(x) << endl;
x= 0xF0; hex(x); cout << dec << pop7(x) << endl;
}
void test1()
{
int x;
x = 0x7FFFFFFF; hex(x); cout<< dec << pop6(x) << endl;
x = 0; hex(x); cout<< dec << pop6(x) << endl;
x = -1; hex(x); cout<< dec << pop6(x) << endl;
x = 1; hex(x); cout<< dec << pop6(x) << endl;
x = 0xABCDE123; hex(x); cout<< dec << pop6(x) << endl;
x = 0x87654321; hex(x); cout<< dec << pop6(x) << endl;
x = 0x55555555; hex(x); cout<< dec << pop6(x) << endl;
x = 0x80808080; hex(x); cout<< dec << pop6(x) << endl;
x = 0xABC5DEFA; hex(x); cout<< dec << pop6(x) << endl;
x = 0X80000000; hex(x); cout<< dec << pop6(x) << endl;
}