统计数字二进制中1的个数(分治法)
对于此问题,《编程之美》中给出了五种解法,但是实际上还有更高效的算法。
可以利用分治的方法解决这个问题。
例如,要计算二进制数 A=0110110010111010 中 1 的个数,这些运算可以表示为:
符号二进制十进制注释A0110110010111010 原始数据B = A & 01 01 01 01 01 01 01 0101 00 01 00 00 01 00 001,0,1,0,0,1,0,0A 隔一位检验C = (A >> 1) & 01 01 01 01 01 01 01 0100 01 01 00 01 01 01 010,1,1,0,1,1,1,1A 中剩余的数据位D = B + C01 01 10 00 01 10 01 011,1,2,0,1,2,1,1A 中每个双位段中 1 的个数列表E = D & 0011 0011 0011 00110001 0000 0010 00011,0,2,1D 中数据隔一位检验F = (D >> 2) & 0011 0011 0011 00110001 0010 0001 00011,2,1,1D 中剩余数据的计算G = E + F0010 0010 0011 00102,2,3,2A 中 4 位数据段中 1 的个数列表H = G & 00001111 0000111100000010 000000102,2G 中数据隔一位检验I = (G >> 4) & 00001111 0000111100000010 000000112,3G 中剩余数据的计算J = H + I00000100 000001014,5A 中 8 位数据段中 1 的个数列表K = J & 000000001111111100000000000001015J 中隔一位检验L = (J >> 8) & 000000001111111100000000000001004J 中剩余数据的检验M = K + L00000000000010019最终答案
//types and constants used in the functions below typedef unsigned __int64 uint64; //assume this gives 64-bitsconst 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 onesconst 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;} //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 &0xff;} //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) + ... }