【问题标题】:Sum of set bits of numbers from 1 to n is atleast k从 1 到 n 的数字的集合位之和至少为 k
【发布时间】:2019-01-07 05:10:07
【问题描述】:

求最小 N 使得从 1 到 N 的数字的 Set 位之和至少为 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

【问题讨论】:

  • 怎么样?我想你已经给出了前 n 个自然数之和的公式。但这里的要求是 ∑(i = 1 to N) (Set Bit Of i)
  • 我的错。现在我已经更正了
  • 好的,that 可能会有所帮助。
  • 我已经看到了,但我不认为它不会有太大帮助,因为范围很大
  • 我希望在我做这一切之前看到来自@RingØ 的链接。我的解决方案与此几乎相同。正如你所说,二分搜索就是答案,而且效果很好——大范围不是问题。

标签: dynamic-programming bit


【解决方案1】:

在 Amadan 的答案之后发布我的答案,因为它对设置为 N 的位数的计算带来了不同的观点;问题的解决是通过二进制搜索进行的,这是适当的,因为位集计算成本低廉


让我们看看 N 是 2 的幂,比如 8
dcba
----
 000
 001
 010
 011
 100
 101
 110
 111
1000 

在列 a 中,我们交替使用 10,在列 b 中相同但每两个 (21)数字,并且在 c 中每四个 (22) 数字。

无论如何,我们在每一列中得到相同数量的1,N/2。因此,1 最多为 2 的幂的数量是(+1 为 2 本身的幂)

log2(N) * N/2 + 1

任何整数都是 2 的幂和,例如 13

1000 + 0100 + 0001

直到 N 的1 的数量是所有 2 个 N 的幂中的每一个的上述等式的总和,在左侧为每个幂添加 1s x = 2P(因为计数到那个幂,幂的高位> P在那里)

bitsets = P * x/2 + 1 + x * 左边设置的位数x次方

例如 1310

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)

1 最多有 25 个13

计算是O(log(N)),速度很快,即使是 1018(大约 60 次迭代)。

二进制搜索将在 O(log2) 中工作,找到最大值 k 是从 1 到 10 以上 2 的幂设置的位数18,通过上面的公式可以计算出来。

【讨论】:

    【解决方案2】:

    假设您的电话号码是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,其中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
    

    希望我没有在任何地方搞砸我的逻辑。顺便说一句,我的代码说从11_000_000_000_000_000_00029761222783429247000 位,循环只有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 条件进行了一些调整,一开始就搞砸了。

    【讨论】:

    • 问题不在于如何计算如此大的值的集合位,而是如何搜索至少k。我如何在不存储总和的情况下进行搜索?
    • 好了,搜索也开始了。
    【解决方案3】:

    喜欢这个问题,所以花了一些时间编写它。 下面的 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-03
      相关资源
      最近更新 更多