计算整数在二进制形式下1的个数
给定一个无符号32位整数, 计算它在二进制形式下1的个数
方法1
循环地检查每一位是否为1, 并计数.
这个是最容易想到的方式, 但是需要循环32次, 性能开销过大
1 2 3 4 5 6 7 8 9 10 11
| func CountBits1(num uint32) (count int) { for num > 0 { if num&0b1 == 1 { count++ } num >>= 1 } return }
|
方法2
从低位开始, 循环地清除1, 直到数字为0.
对于那些1的个数少的数字,可以大大降低循环的次数
1 2 3 4 5 6 7 8 9
| func CountBits2(num uint32) (count int) { for num > 0 { num &= num - 1 count++ } return }
|
方法3
类似于归并排序的思想, 我们先将相邻的两个比特位相加, 然后将相邻的两个结果相加, 不断重复将相邻的结果相加,直至最后只剩下一个结果.
比如对于二进制数0b10001111
, 如下图,可以计算出1的个数为5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| graph BT; 1(101) 1-->2(1) 1-->3(100) 2-->4(1) 2-->5(0) 3-->6(10) 3-->7(10) 4-->8(1) 4-->9(0) 5-->10(0) 5-->11(0) 6-->12(1) 6-->13(1) 7-->14(1) 7-->15(1)
|
1 2 3 4 5 6 7 8 9
| func CountBits3(num uint32) (count int) { num = num&0x55555555 + num>>1&0x55555555 num = num&0x33333333 + num>>2&0x33333333 num = num&0x0f0f0f0f + num>>4&0x0f0f0f0f num = num&0x00ff00ff + num>>8&0x00ff00ff num = num&0x0000ffff + num>>16&0x0000ffff return int(num) }
|
性能测试
我们取固定的数0b11001111110011111100111111001111
进行测试
通过benchmark的结果为:
1 2 3 4 5 6 7 8
| ➜ igos go test -bench=. goos: linux goarch: amd64 pkg: github.com/souhup/igos BenchmarkCountBits1-4 87418681 13.5 ns/op BenchmarkCountBits2-4 144392588 8.16 ns/op BenchmarkCountBits3-4 1000000000 0.435 ns/op PASS
|