从1到n的数字的置位总和至少为k

时间:2019-01-07 05:10:08

标签: dynamic-programming bit

找到最小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秒

3 个答案:

答案 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,其中bn(即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 * yy << 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

希望我没有在任何地方弄乱我的逻辑。顺便说一句,我的代码说从2976122278342924700011_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的位数方面带来了不同的观点;问题的解决方案是通过二进制搜索来进行,这是合适的,因为位集计算成本不高


假设N是2的幂,例如8

dcba
----
 000
 001
 010
 011
 100
 101
 110
 111
1000 

a 列中,我们在{em> b 列中也有10相同,但每两个(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