最近看阮一峰老师的每周科技周刊, 发现一个有意思的算法1, 具体的方法文章中都写了, 不过这里还是介绍一下具体的思路以及其Python版的实现.
一般来说, 计算位1 的个数可以通过下面的两种方法:
def calcbit1_v1(n):
return bin(n).count("1")
def calcbit1_v2(n):
ans = 0
while n:
tmp = n & 1 # 取最末位
ans += tmp
n >>= 1 # 进位
return ans
文中给出的方法是下面这样的:
def calcbit1_v3(n):
total = 0
tmp = n
while tmp:
tmp >>= 1
total += tmp
return n - total
def calcbit1_v4(n):
diff = n
while n:
n >>= 1
diff -= n
return diff
其中
v3是为了解释v4.
下面来看一下为什么这样可以计算出整数二进制表示中1的个数.
这个方法基于下面的一个式子:(从二进制表示形式更容易理解)
n
=
n
2
+
n
4
+
n
8
+
⋯
=
∑
k
=
0
+
∞
n
2
k
n=\frac n2+\frac n4+\frac n8+\cdots=\sum_{k=0}^{+\infty}\frac n{2^k}
n=2n+4n+8n+⋯=k=0∑+∞2kn
其中
n
n
n是实数.
当
n
n
n为正整数的时候, 公式就退化成:
n
2
+
n
4
+
n
8
+
⋯
≈
n
\frac n2+\frac n4+\frac n8+\cdots\approx n
2n+4n+8n+⋯≈n
只能找到一个最接近的项数, 使值最接近
n
n
n.
从二进制角度, 我们从最低位开始,
0, 右移之后剩下的数与原来的数相比仅仅是做了除以2的操作, 不会留下1, 所以直接加和即可.所以, 保留每次右移运算之后剩下的数之和, 然后与原数n相减, 那么就能得到位1的个数了.
这里剩下的数其实也可以理解为
余数, 因为右移本质上是整除2.
因为整数的右移一位就相当于整除2, 每移出一个位,总和就减少1。这意味着计算和与数学和之间的差等于设定位的数量。
例如, 对于
n
=
11
n=11
n=11, 其二进制表示为1011,
tmp=5, total=5,tmp=2, total=7,tmp=1, total=8,n减去total, 这就得到了数11的位1个数为3.这个方法更加简便, 省去了一步减的操作, 除此之外在思路上与v3一致.