什么是获得第n个整数的快速方法> 0,最多设置k位?

时间:2016-06-23 16:41:36

标签: c# math bits

我有一个系列,值大于0,其中设置的位数不超过k

for instance, for `k = 2`

n    binary    value    bits_set
0    0000      0        0
1    0001      1        1
2    0010      2        1
3    0011      3        2
4    0100      4        1
5    0101      5        2
6    0110      6        2
7    1000      8        1
8    1001      9        2
9    1010      10       2
10   1100      14       2
... etc ...

对于给定值n,是否有计算效率的方法来查找系列中的k第?项?

我所有的尝试都表现得很慢,而且我不知道如何有效地解决这个问题。

1 个答案:

答案 0 :(得分:1)

我知道这个问题被标记为C#,对于在Python中给出答案表示道歉:

首先,我们将找到一种方法来计算有多少不同的数字与可用空间中的一定数量的位。然后我们将找到一种排序结果的方法。

将f(b,s)表示为构造b集合中正好s位的数字的方式。

这里有一个递归关系。满足f(b,s)的数字或者是满足f(b-1,s)的数字,其前面是0,或者是满足f(b-1,s-1)且前面有1的数字。因此f(b,s)= f(b-1,s)+ f(b-1,s-1)。

一些基本案例填写该表: f(b,0)为1 f(b,s)为1,其中b = s

    10 9 8 7 6 5 4 3 2 1 0
24   . . . . . . . . . . 1
23   . . . . . . . . . . 1
22   . . . . . . . . . . 1
21   . . . . . . . . . . 1
20   . . . . . . . . . . 1
19   . . . . . . . . . . 1
18   . . . . . . . . . . 1
17   . . . . . . . . . . 1
16   . . . . . . . . . . 1
15   . . . . . . . . . . 1
14   . . . . . . . . . . 1
13   . . . . . . . . . . 1
12   . . . . . . . . . . 1
11   . . . . . . . . . . 1
10   1 . . . . . . . . . 1
 9   0 1 . . . . . . . . 1
 8   0 0 1 . . . . . . . 1
 7   0 0 0 1 . . . . . . 1
 6   0 0 0 0 1 . . . . . 1
 5   0 0 0 0 0 1 . . . . 1
 4   0 0 0 0 0 0 1 . . . 1
 3   0 0 0 0 0 0 0 1 . . 1
 2   0 0 0 0 0 0 0 0 1 . 1
 1   0 0 0 0 0 0 0 0 0 1 1

构建表g(b,s)也很有用,它表示有多少b位数设置了s或更少的位。 g(b,s)= sum(i = 0到s)f(b,i)

所以我们现在可以回答一个问题,那就是有多少个数字,其中有10个数据位是24,那就是f(24,10)= 1961256,我们可以回答最多10位数的数量在24中,那是f(24,10)+ f(24,9)+ f(24,8)...... + f(24,1)+ f(24,0)= g(24,10)= 4540386

但是,如果问题是找到第n个数字,使得最多设置24个中的10个,我们需要能够以有序的方式搜索这个空间。

首先,请注意,第n位中第一个1的任何数字都大于n中任何位的前1位数。

这意味着我们可以找到每个数字的位置,只需要一个位集,然后是z零。这必然大于g(z,max(z,10))。我们可以在这里进行优化并说明10位空间中的所有数字都是合格的(它们不可能设置超过10位)然后第n个这样的数字= n对于所有n< = 2 ^ 10

如果n>通过找到最大的z_10使得g(z_10,10)<= n,我们可以搜索以找到第一设置比特的位置。如果它实际上等于n,我们找到了答案,所以可以停止。如果我们想要超高效,甚至可以通过二分搜索来完成!

否则,我们必须找到最大的z_9,使得g(z_9,9)&lt; = n - g(z_10,10),然后是最大的z_8,使得g(z_8,8)&lt; = n - g( z_10,10) - g(z_9,9)等等,直到我们达到平等并回答了问题。

在Python中:

class Memoize:
    def __init__(self, f):
        self.f = f
        self.memo = {}
    def __call__(self, *args):
        if not args in self.memo:
            self.memo[args] = self.f(*args)
        return self.memo[args]

def g(b, s):
    r = 0
    for i in range(0, s+1):
        r = r + f(b, i)
    return r

def f(b,s):
    if b == s:
        return 1
    if s == 0:
        return 1
    if b < s:
        return 0
    return f(b-1, s) + f(b-1, s-1)

def build(s, n, i):
    d = (24 - len(s))
    if n <= 2 ** i:
        return s + format(n, '0%db' % (d))
    for z in range(i, d+1):
        x = g(z, i)
        if x < n:
            continue
        if x == n:
            return s + format(2 ** z, '0%db' % (d))
        y = g(z-1, i)
        return build(s + format(1, '0%db' % (d-(z-1))),
                     n - y,
                     i - 1)

def solve(n):
    return build("", n-1, 10)

f = Memoize(f)
g = Memoize(g)

for n in range(1, 4540387):
    print("%07d: %s" % (n, solve(n)))