如何计算一系列数字[A,B]的设定位的倒数之和 (这里A,B> 0& A,B< 10 ^ 9)?
我的方法:
通过使用从A到B的简单for循环,我使用_builtin_popcount(一个用于计算C / C ++中数字的setbits的内置函数)计算了一个数字的setbits,然后取其倒数并添加。这是一种O(n)方法。但是由于更大的约束,它需要更长的时间。如何进一步优化?是否可以使用O(log(n))算法?
答案 0 :(得分:2)
让F(N, k) = |{m | m is an integer lying in [0, N] and m's binary representation has exactly k bits set}|
。您想要的答案是SUM{ (F(B,k) - F(A-1,k))/k | 1<=k<=MSB(B)}
,其中MSB
=最重要的位。
您可以递归计算F(N,k)
。正确处理递归的边界。实际的递归是
F(N, k) = F(N^(1<<MSB(N)), k-1) + F((1<<MSB(N))-1, k)
简而言之,您认为那些与MSB
具有相同N
的数字和MSB
与N
相同的数字并递归。
运行时为O(log(B)*log(B))
。
编辑:说明递归:
N = 1101
,二进制,k=2
。
<= N
与MSB
相同的数字N
为{1000, 1001, 1010, 1011, 1100, 1101}
。请注意,它们实际上与此集1000 + {000, 001, 010, 011, 100, 101}
相同。换句话说,它们都是数字<= N^(1<<MSB(N)) = 1101 ^ 1000 = 101
。由于您已经计算了MSB
位,因此集合{000, 001, 010, 011, 100, 101}
中所需的位数为k-1
。这解释了F(N^(1<<MSB(N)), k-1)
一词。
<=N
的{{1}}小于MSB
的数字N
为{000, 001, 010, 011, 100, 101, 110, 111}
。换句话说,所有数字<= (1<<MSB(N)) - 1 = 1000 - 1 = 111
。到目前为止,您还没有计算任何设置位。因此,您仍需要集合k
中的数字{000, 001, 010, 011, 100, 101, 110, 111}
位。这就是F((1<<MSB(N))-1, k)
术语的来源。