swar算法
uint32_t swar(uint32_t i){
i = (i & 0x55555555) + ((i>>1) & 0x55555555); // 步骤1
i = (i & 0x33333333) + ((i>>2) & 0x33333333); // 步骤2
i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F); // 步骤3
i = (i * 0x01010101) >> 24; // 步骤4
return i;
}
把i的二进制位理解成:长度为32的数组,每个元素取值区间[0,1],每个元素正好能代表这个位是不是1.
问题转化为:求这个数组的和,也就是1的个数
规定:一个32bit数字,从右到左,依次是第1位,第2位,…,第32位 (虽然跟计算机的标记有出入)
i & 0x55555555 = i & 0b 0101 0101 0101 0101 0101 0101 0101 0101 把奇数位剥离出来
i & 0xaaaaaaaa = i & 0b 1010 1010 1010 1010 1010 1010 1010 1010 把偶数位剥离出来
i = i & 0x55555555 + i & 0xaaaaaaaa 看证明:
i = i & (0x55555555 + 0xaaaaaaaa) = i & 0xffffffff = i
偶数位剥离出来后,第1位、第3位、…、第31位铁定为0
将剥离出来的偶数位转换为奇数位,变成了 (i>>1) & 0x55555555,这个结果会让偶数位的1都跑到奇数位上去
这样 (i & 0x55555555) 、 (i>>1) & 0x55555555 的偶数位都为0,奇数位都是i之前的bit
(i & 0x55555555) 瓜分了i的所有奇数位
(i>>1) & 0x55555555 瓜分了i的所有偶数位,瓜分完后对齐到新的32bit的奇数位上
(i & 0x55555555) + (i>>1) & 0x55555555 后,1的个数和全部落在每2bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为2,可用2bit全部表示,但最大值为 0b10
同理:
(i & 0x33333333) + ((i>>2) & 0x33333333) 之后,1的个数和全部落在每4bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为4,可用3bit全部表示,但最大值为 0b0100
(i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F) 之后,1的个数和全部落在每8bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为8,可用4bit全部表示,但最大值为 0b1000
这样,每8bit中,高4bit全是0,低4bit表示这8bit中1的个数
所以才能对这8bit直接相加
最后将每字节的数字相加,就是1的个数和
i = (i * 0x01010101) >> 24 = (i * 0x01000000 + i * 0x00010000 + i * 0x00000100 + i * 0x00000001) >> 24
= (i * 2^0 + i * 2^8 + i * 2^16 + i * 2^24) >> 24
= (i + i << 8 + i << 16 + i << 24) >> 24
= i >> 24 + (i << 8) >> 24 + (i << 16) >> 24 + (i << 24) >> 24
这里左移右移的目的在于只把当前8bit的数字留下,所有高8bit的数字全部清0
所以才能直接相加
i >> 24 :留下了最高8bit
(i << 8) >> 24 :将最高8bit清0,留下了次高8bit
(i << 16) >> 24 :将最高16bit清0,留下了次低8bit
(i << 24) >> 24 :将最高24bit清0,留下了最低8bit
这里用到了分治法的思想。
扩展
i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F) 之后,1的个数和全部落在每8bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为8,可用4bit全部表示,但最大值为 0b1000 = 2^3
可以继续往下分治:
i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF) 之后,1的个数和全部落在每16bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为16,可用5bit全部表示,但最大值为 0b10000 = 2^4
i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF) 之后,1的个数和全部落在每32bit一个单元的块上
相加之后,每一个单元块上,1的个数最多为32,可用6bit全部表示,但最大值为 0b100000 = 2^5
此时 i 的最低8位存储的就是最初的i中的1的个数
实例
#include <stdio.h>
unsigned int swar1(unsigned int i){
i = (i & 0x55555555) + ((i>>1) & 0x55555555);
i = (i & 0x33333333) + ((i>>2) & 0x33333333);
i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
i = (i * 0x01010101) >> 24;
return i;
}
unsigned int swar2(unsigned int i){
i = (i & 0x55555555) + ((i>>1) & 0x55555555);
i = (i & 0x33333333) + ((i>>2) & 0x33333333);
i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF);
i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF);
return i;
}
int main()
{
printf("Hello, World! %d\n", swar1(0xffffffff));
printf("Hello, World! %d\n", swar2(0xffffffff));
return 0;
}
其他算法
unsigned int count3(unsigned int i){
int count = 0;
while (i) {
i &= (i-1);
count++;
}
return count;
}