假设您的电话号码是13。在二进制中,它是1101。让我们把它放在表格上,看看我们是否能看到模式。稍后我将插入一些换行符以提供帮助。另外,我会添加0,因为它不会造成伤害(它没有设置位)。
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1011
我们可以这样写n下的所有组:
000
001
010
011
100
101
110
111
1000 + 00
1000 + 01
1000 + 10
1000 + 11
1000 + 100 + | (no digits, equal to 0 in sum)
请注意,对于n=1101 的第三位,我们有一组大小为2^3 的组;我们有一组大小为2^2 的第二位;和一组大小为2^0 的LSB。我们称组大小为s=2^b,其中b 是n 中所有设置位的位置(即b=[3, 2, 0], s=[8, 4, 1])。注意每组中最右边的和的位模式:有b 列的位,并且在每一列中都设置了s/2;因此,对于每个设置位,最右边的列中有2^(b-1)*b 设置位。但是每一行也有等于组序数的设置位数:0、1、2(当我们添加与n 中的设置位相对应的组时),每个组总共有2^b*i + 2^(b-1)*b 设置位:
Group 0: 2^3*0 + 2^2*3 = 12
Group 1: 2^2*1 + 2^1*2 = 8
Group 2: 2^0*2 + 2^(-1)*0 = 2
这是所有设置位的数量最多n-1。要获得n 的位数,我们需要为n 本身中设置的每个位再获得一个位 - 这正是我们拥有的组数。因此总数为12+8+2 +3 = 25。
这里是 Ruby。请注意,2^x * y 与 y << x 相同。
def sum_bits_upto(n)
set_bits = n.to_s(2).reverse.each_char.with_index.map { |c, b|
b if c == "1"
}.compact.reverse
set_bits.each_with_index.sum { |b, i|
(i << b) + (b << b - 1) + 1
}
end
希望我没有在任何地方搞砸我的逻辑。顺便说一句,我的代码说从1 到1_000_000_000_000_000_000 有29761222783429247000 位,循环只有24 次迭代。那应该足够快:)
编辑:显然我有金鱼记忆。总和是单调递增的(对于每个连续的数字,都会有一个正数的位添加到运行总数中)。这意味着,我们可以使用二分搜索,即使我们不能通过存储中间结果来帮助它(在我的 Mac 上执行时间为 0.1 秒),它也应该在目标上快速归零:
max = 1_000_000_000_000_000_000_000_000_000
k = 1_000_000_000_000_000_000
n = (1..max).bsearch { |n|
sum_bits_upto(n) >= k
}
# => 36397208481162321
必须有一个很好的公式来计算基于k 搜索的理论上可能的最大 n,但我不会被打扰,所以我只输入了足够大的东西。 bsearch 这么好。
EDIT2:对bsearch 条件进行了一些调整,一开始就搞砸了。