计算汉民重量

168 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。 用于统计一个位数组中非0二进制位的数量。

 static int swar(int i) {
  	 // 每两个一组,每一组的数字表示这一组中有几个1
     i = (i&0x55555555) + ((i>>>1)&0x55555555);
     // 每4个一组,这个数据是根据上一个数据计算出来的
     i = (i&0x33333333) + ((i>>>2)&0x33333333);
     // 同理,每8个一组记录1的个数
     i = (i&0x0f0f0f0f) + ((i>>>4)&0x0f0f0f0f);

     i = (i*(0x01010101)) >>> 24;
     return i;
 }

举例:

每两个一组 0x55(16进制) => 0x0101,0101(2进制) i取值0x1101,1010

(i&0x55) + ((i>>>1)&0x55);

 i&0x55

 0x1101,1010
&0x0101,0101
 0x0101,0000

(i>>>1)&0x55

 0x0110,1101
&0x0101,0101
 0x0100,0101

最终结果:
原值:0x11 01 10 10
新值:0x10 01 01 01  注意对应的是每组1的个数

每4个一组、每8个一组同理

比较难理解的点

i = (i*(0x01010101)) >>> 24;
拆分来看
(i*(0x01010101))
满足乘法分配律
(i*(0x01000000)) + (i*(0x00010000)) + (i*(0x00000100)) + (i*(0x00000001))

A, B, C, D 每一个字母代表 8 位数, 那么 32 为数可以被 (A, B, C, D) 表示
0x01010101 * (A, B, C, D ) = (A+B+C+D, B+C+D, C+D, D)

根据乘法分配律
i*0x0100 0000 + i*0x0001 0000 + i*0x0000 0100 + i*0x0000 0001

i*0x0100 0000 实际上等于左移24位
 D, _, _, _     i*0x0100 0000
+C, D, _, _     i*0x0001 0000
+B, C, D, _     i*0x0000 0100
+A, B, C, D     i*0x0000 0001
A+B+C+D, B+C+D, C+D, D