计算设置第i位的1到N(含)之间的数字

时间:2019-06-02 17:26:28

标签: algorithm bit-manipulation bitmask

我想计算在1和 N 之间有多少个整数设置了它们的 i 位。例如,如果 N = 10和 i = 0,则结​​果应为5(因为1 = 0001 2 ,3 = 0011 < sub> 2 ,5 = 0101 2 ,7 = 0111 2 和9 = 1001 2 各自在位为1 0)。

朴素的线性时间解决方案是从1迭代到 N ,然后为每个数字查看是否设置了第 i 位。

一种稍微好一点的方法是,因为已知功率为2(例如2 x ),所以2 x - 1 号的第 i 位将被置位,直到数字2 x -1,其中0≤i N −2 x )开头的第 i 位设置所有数字。 em> N 是直到找到所有具有第 i 位的数字为止的数字,而2 x 是数字 N 的最接近2的幂。这种方法减少了迭代次数,但仍然是线性时间解决方案,在某些情况下,对于较大的次数可能非常无用。

有固定时间的解决方案吗?

1 个答案:

答案 0 :(得分:4)

首先让我们看一个例子。如果我们设置n=10,然后看第二位,那么k=1从右边看,我们将看到:

0000    0
0001    0
0010    1
0011    2
0100    2
0101    2
0110    3
0111    4
----
1000    4
1001    4
1010    5

我们在这里看到 k ⌊N/ 2 k + 1 次完整的往返 >位,并且每次这样的往返都会导致 2 k 位。我们将这些条目分组在水平条之前。

此外,还有 N +1-2 k + 1 ×⌊N/ 2 k + 1 条目在单杠下。我们肯定知道它比 2 k ,因为否则⌊N/ 2 k 会高一。前 2 k-1 项具有0作为选定位,而其余位(最多 2 k-1 项)具有1作为选定位。

因此,我们可以在Haskell中构造以下算法:

countBit k n = c1 +  max 0 (n + 1 - c0 - sk)
    where sk = shiftL 1 k
          c1 = shiftL (shiftR n (k+1)) k
          c0 = shiftL c1 1

例如,k=1的计数如下:

Prelude Data.Bits> map (countBit 0) [0..32]
[0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16]
Prelude Data.Bits> map (countBit 1) [0..32]
[0,0,1,2,2,2,3,4,4,4,5,6,6,6,7,8,8,8,9,10,10,10,11,12,12,12,13,14,14,14,15,16,16]
Prelude Data.Bits> map (countBit 2) [0..32]
[0,0,0,0,1,2,3,4,4,4,4,4,5,6,7,8,8,8,8,8,9,10,11,12,12,12,12,12,13,14,15,16,16]
Prelude Data.Bits> map (countBit 3) [0..32]
[0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,8,9,10,11,12,13,14,15,16,16]
Prelude Data.Bits> map (countBit 4) [0..32]
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16]

因此对于n=10k=1,我们得到了预期的结果:

Prelude Data.Bits> countBit 0 10
5
Prelude Data.Bits> countBit 1 10
5

或者我们可以用k=3列计算从012345(包括)在内的数字的设置位数:

Prelude Data.Bits> countBit 3 12345
6170

或针对k=15n=12'345'678'901'234'567'890

Prelude Data.Bits> countBit 15 12345678901234567890
6172839450617282560

n=123'456'789'012'345'678'901'234'567'890

Prelude Data.Bits> countBit 15 123456789012345678901234567890
61728394506172839450617282560

我们在这里执行一些移位和减法,对于大量移位可以在 O(log N)时间(以上限为 N )完成。