找到最小N,以使从1到N的数字的置位总和至少为k。
例如
k = 11, output N = 7, as SB(1) + SB(2) + .. +SB(7) = 12
k = 5, output N = 4, as SB(1) + SB(2) +..+SB(4) = 5
我想先解决这一问题,方法是存储设置的位之和,然后对至少k进行二进制搜索。但是,这里的问题是1 <= k <= 10 ^ 18。因此,显然不能使用DP。那么如何解决这个问题。时限为1.5秒
答案 0 :(得分:1)
假设您的电话号码是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)
请注意,2^3
的第3位有一组大小为n=1101
的组;我们第二组有一组2^2
号的组; LSB的大小为2^0
。让我们称之为组大小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
希望我没有在任何地方弄乱我的逻辑。顺便说一句,我的代码说从29761222783429247000
到1
有1_000_000_000_000_000_000
位,循环只有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
搜索的max n,但是我不为所动,所以我只写了足够大的东西。 bsearch
就是这样。
EDIT2:针对bsearch
条件的几处调整,一开始就搞砸了。
答案 1 :(得分:1)
将我的答案发布在Amadan的答案之后,因为它在设置为N的位数方面带来了不同的观点;问题的解决方案是通过二进制搜索来进行,这是合适的,因为位集计算成本不高
dcba
----
000
001
010
011
100
101
110
111
1000
在 a 列中,我们在{em> b 列中也有1
和0
相同,但每两个(2 1 < / sup>)数字,并在 c 中每四个(2 2 )个数字出现。
无论如何,我们在每一列中获得相同数量的1
,N / 2。因此,1
的幂为2的数目为(+1为2的幂本身)
log 2 (N)* N / 2 + 1
任何整数都是2的幂的和,例如13
1000 + 0100 + 0001
直到N的1
的数量是N的所有2个幂中的每一个的上述公式的总和,在左边将每个幂 x < / em> = 2 P (因为 count 达到该幂,所以存在幂> P的较高位)
位集= P * x / 2 + 1 + x * 在左侧设置该幂x的位数x
例如13 10
1
从1000 => 3 x 8/2 +1 = 13 + 0 (no 1 left)
0100 => 2 x 4/2 +1 = 5 + 4 x 1 (one bit on the left, the 8)
0001 => 0 x 1/2 +1 = 1 + 1 x 2 (the 8 and the 4)
到25 1
。
计算速度为13
,即使是10 18 (约60次迭代),计算速度也很快。
二进制搜索将在O(log 2 )中工作,找到最大 k 是设置为1的位数,直到10的2的幂次< sup> 18 ,由于上面的公式可以计算出来。
答案 2 :(得分:1)
喜欢这个问题,所以花了一些时间进行编码。 以下Python代码适用于位位置,因此复杂度仅限于10 ^ 18中存在的最大位数。
# Store sum of 1-bits upto max number formed by N bits.
# For example - sumToNBits of 1 bit is 1, 2 bit numbers 01,10,11 is 4 and 3 bit
# numbers 01,10,11,100,101,110,111 is 12
# and so on.
sumToNBits = []
prevSum = 0
for i in range(1, 65):
prevSum = (1 << (i-1)) + 2*prevSum
sumToNBits.append(prevSum)
# Find maximum possible number (2 to power P - 1) which has sum of 1-bits up to K.
def findClosestPowerTwo(K):
index = 1
prevEntry = 0
for entry in sumToNBits:
if (entry > K):
return (K-prevEntry, index-1)
prevEntry = entry
index += 1
return (K-prevEntry, index-1)
# After finding max 2 to power P, now increase number up to K1 = K - bits used by 2 to power P.
def findNextPowerTwo(K, onBits):
index = 1
prevTotalBits = 0
totalBits = onBits * ((1 << index) - 1) + sumToNBits[index-1]
while(K >= totalBits):
prevTotalBits = totalBits
index += 1
totalBits = onBits * ((1 << index) - 1) + sumToNBits[index-1]
return (K-prevTotalBits, index-1)
def findClosestNumber(K):
(K, powerTwo) = findClosestPowerTwo(K)
number = (1 << (powerTwo)) - 1
# Align to 2 to power P
if (K >= 1):
K = K - 1
number += 1
onBits = 1
while(K > 0):
(K, powerTwo) = findNextPowerTwo(K, onBits)
if (powerTwo == 0):
return number+1
number += ((1 << (powerTwo)) - 1)
# Align to 2 to power P
if (K >= (onBits + 1)):
K = K - (onBits + 1)
number += 1
onBits += 1
return number
num = 1
while num < 100:
print(num, end = " ")
print(findClosestNumber(num))
num += 1