Hamming Weight的算法分析（转载）

Hammingcode是指一个字串中非0符号的个数（TheHamming weight of a stringis the number of symbols that are different from the zero-symbol ofthealphabetused.）。应用到2进制符号序列中来，即二进制串中1的个数就是该串的Hammingcode.那么上述的问题即转换成求解字串的Hammingcode的问题。

[cpp] view plaincopy

//types and constants used in the functions below

typedef unsigned __int64 uint64;  //assume this gives 64-bits
const uint64 m1  = 0x5555555555555555; //binary: 0101...
const uint64 m2  = 0x3333333333333333; //binary: 00110011..
const uint64 m4  = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...
const uint64 m8  = 0x00ff00ff00ff00ff; //binary:  8 zeros,  8 ones ...
const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones
const uint64 hff = 0xffffffffffffffff; //binary: all ones
const uint64 h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...

//This is a naive implementation, shown for comparison,
//and to help in understanding the better functions.
//It uses 24 arithmetic operations (shift, add, and).
int popcount_1(uint64 x) {
x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits
x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits
x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits
x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits
x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits
x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits
return x;
}  

x&m1 = 0b0d0f0h
(x>>2)&m1 = 0a0c0e0g

x&m2 = 00[c+d]200[g+h]2
(x>>4)&m2 = 00[a+b]2 00[e+f]2

x&m4 = 0000[e+f+g+h]4
(x>>4)&m2 = 0000[a+b+c+d]4

[cpp] view plaincopy

//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64 x) {
x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits
x += x >>  8;  //put count of each 16 bits into their lowest 8 bits
x += x >> 16;  //put count of each 32 bits into their lowest 8 bits
x += x >> 32;  //put count of each 64 bits into their lowest 8 bits
return x & 0x7f;
}  

popcount2在popcount1的基础上进行了优化。

[cpp] view plaincopy

//This uses fewer arithmetic operations than any other known
//implementation on machines with fast multiplication.
//It uses 12 arithmetic operations, one of which is a multiply.
int popcount_3(uint64 x) {
x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits
return (x * h01)>>56;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}  

popcount3进一步进行了优化，只看最后一步:return (x * h01)>>56;

x*h01 = x*0x0101010101010101 = x+(x<<8)+(x<<16)...+(x<<56)

[cpp] view plaincopy

//This is better when most bits in x are 0
//It uses 3 arithmetic operations and one comparison/branch per "1" bit in x.
int popcount_4(uint64 x) {
int count;
for (count=0; x; count++)
x &= x-1;
return count;
}  

[cpp] view plaincopy

//This is better if most bits in x are 0.
//It uses 2 arithmetic operations and one comparison/branch  per "1" bit in x.
//It is the same as the previous function, but with the loop unrolled.
#define f(y) if ((x &= x-1) == 0) return y;
int popcount_5(uint64 x) {
if (x == 0) return 0;
f( 1) f( 2) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8)
f( 9) f(10) f(11) f(12) f(13) f(14) f(15) f(16)
f(17) f(18) f(19) f(20) f(21) f(22) f(23) f(24)
f(25) f(26) f(27) f(28) f(29) f(30) f(31) f(32)
f(33) f(34) f(35) f(36) f(37) f(38) f(39) f(40)
f(41) f(42) f(43) f(44) f(45) f(46) f(47) f(48)
f(49) f(50) f(51) f(52) f(53) f(54) f(55) f(56)
f(57) f(58) f(59) f(60) f(61) f(62) f(63)
return 64;
}

//Use this instead if most bits in x are 1 instead of 0
#define f(y) if ((x |= x+1) == hff) return 64-y;  

static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount(uint32 i)
{
return (wordbits[i&0xFFFF] + wordbits[i>>16]);
}


Hamming Weight还有很多应用，这里只是简单记录一下它在求解popcount上的用法。

posted @ 2015-03-18 19:43 禅意 阅读(...) 评论(...) 编辑 收藏