Count 1s in Range
在 codefights 上做到了一个很有意思的题。给你 a, b 两个数,假设你构建了一个从 a 到 b 的 array,所有这些数的 binary representation 里面有多少个 1?
Naive Solution #
最简单的办法 (随手翻了两个别人的答案都是这个), 就是一个数一个数查有多少 1. 笨一点的手工查, 聪明一点的用 built-in function 查. 但是无论如何, 复杂度都是 O(b - a).
Log(n) Solution #
我们先思考一个简化版本的问题: 所有
unsigned int (c++)
里面有多少 1? 答案是所有的 bit / 2,
因为当我们把所有数都写成 2 进制, 一半是 1, 一半是0.
我们 generalize 一下: 对于任意的 $m$, 所有 $m$ 位的正整数里有 $(2^m * m) / 2$.
当我们的上限不是位数,而是一个数字 $b$, 我们可以把 $b$ 分成 3 各部分。假设 $b$ 不为 0, $b$ 的二进制形式应该是 $1xxxx$… 假设 $b$ 有 $m + 1$ 位.
现在以 $100…0$ 为 line
:
- line 一个 ‘1’
- below line: 所有 $m$ 位数有$ (2^m * m) / 2$ 个 ‘1’. 这个数从前面的结论得来.
- above line: 除去最左边的 $1$, 右边的部分又变成从 $0$ 开始查. 我们可以用一个简单地递归来解决这个部分。
pseudo code:
function allBits(b) {
if (b < 0) return 0; // 不考虑负数
if (b < 2) return b; // basecase: 只有一位
m = num_of_bits(b) - 1;
line = 1 << m;
onesBelowLine = m * (line - 1) / 2;
onesAboveLine = allBits(b - line) + (b - line);
return 1 + onesBelowLine + onesAboveLine;
}
下面回到原始问题,给定底线和上限,a, b, 我们可以简单地转换一下:
function rangeBitCount(a, b)
{
return allBits(b) - allBits(a);
}
复杂度证明 #
这个算法的复杂度很简单,因为这个 recursion 可以很容易的转换成 tail recursion,再变成复杂度相同的 iteration. 这个 iteration 每次去掉最高位的 1,因此最多有 $\log(b)$ 次循环;每次循环做了 constant 的基本算数,因此最后的复杂度应该是 $\log(b)$。
C++ Source Code #
int allBits(int b) {
if (b < 0) return 0;
if (b < 2) return b;
int num_bit = 31 - __builtin_clz((unsigned) b);
int line = (1 << num_bit);
int count_below = (num_bit * line) / 2;
int above = b - line;
int count_above = above + allBits(above);
return count_above + count_below + 1;
}
int rangeBitCount(int a, int b) {
return allBits(b) - allBits(a - 1);
}