【问题标题】:Returning i-th combination of a bit array返回位数组的第 i 个组合
【发布时间】:2012-08-14 16:36:25
【问题描述】:

给定一个固定长度的位数组以及它包含的 0 和 1 的数量,我如何安排所有可能的组合,以便返回第 i 个组合花费尽可能少的时间? 它们返回的顺序并不重要。

这是一个例子:

array length = 6
number of 0s = 4
number of 1s = 2

可能的组合(6!/4!/2!) 000011 000101 000110 001001 001010 001100 010001 010010 010100 011000 100001 100010 100100 101000 110000

问题 第一个组合 = 000011 第 5 个组合 = 001010 第 9 个组合 = 010100

采用不同的安排,例如 100001 100010 100100 101000 110000 001100 010001 010010 010100 011000 000011 000101 000110 001001 001010

它会返回 第一个组合 = 100001 第 5 个组合 = 110000 第 9 个组合 = 010100

目前我正在使用 O(n) 算法来测试每个位是 1 还是 0。问题是我需要处理很多非常长的数组(大约 10000 位),所以它仍然很慢(而且缓存是不可能的)。我想知道您是否认为可能存在更快的算法。

谢谢

【问题讨论】:

  • 也许这个答案会对你有所帮助:stackoverflow.com/a/311716/220060
  • 你最终要返回多少种组合?
  • 只是所有可能性中的一小部分(当然,因为它们是 2^10000),但即使它们只有 1000 也需要很多时间。
  • 嗯。我不认为我理解这个问题。如果集合的顺序无关紧要,那么在您的示例中,您不能返回任何 2 位设置为第一个组合的 6 位值,以及任何 2 位设置的 6 位值(您尚未选择)作为第 5 个组合等?
  • 谢谢 nalply,但我无法理解该代码与此问题的关系。我的问题是我有一个集合(如上所述),我需要准确提取第 i 个和第 j 个和第 n 个组合。第 i 个或第 j 个是什么并不重要,只要算法是一致的(也就是说,如果我要求第 i 个它总是返回相同的)并且可能比 O(n) 运行得更快(也许 O (ln n) 甚至 O(1))

标签: algorithm bit-manipulation combinations


【解决方案1】:

我不确定我是否理解这个问题,但如果你只想要第 i 个组合而不生成其他组合,这里有一个可能的算法:

有 C(M,N)=M!/(N!(M-N)!) 个 N 位组合设置为 1,最高位在位置 M。

你想要第 i 个:你迭代地递增 M 直到 C(M,N)>=i

while( C(M,N) < i ) M = M + 1

这将告诉您设置的最高位。 当然,你用迭代计算组合

C(M+1,N) = C(M,N)*(M+1)/(M+1-N)

一旦找到,你就会遇到一个问题,即要找到第 (i-C(M-1,N)) 个 N-1 位的组合,所以你可以在 N 中应用递归...
这是一个可能的变体,D=C(M+1,N)-C(M,N),并且 I=I-1 使其从零开始

SOL=0
I=I-1
while(N>0)
    M=N
    C=1
    D=1
    while(i>=D)
        i=i-D
        M=M+1
        D=N*C/(M-N)
        C=C+D
    SOL=SOL+(1<<(M-1))
    N=N-1
RETURN SOL

如果你有那么多位,这将需要大整数运算......

【讨论】:

    【解决方案2】:

    如果排序无关紧要(它只需要保持一致),我认为最快的做法是让组合(i)返回任何你想要的具有所需密度的东西第一次 使用参数 i 调用组合()。然后将该值存储在成员变量中(例如,将值 i 作为键并将您返回的组合作为其值的哈希图)。第二次调用combination(i),你只需在hashmap中查找i,找出你之前返回的内容,然后再次返回。

    当然,当您返回参数(i)的组合时,您需要确保它不是您之前为其他参数返回的东西。

    如果您将被要求返回的数字明显小于组合的总数,那么第一次调用组合(i)的一个简单实现是随机生成一个全为 0 的正确长度的值将位的 num_ones 设置为 1,然后确保它不是您已经为不同的 i 值返回的位。

    【讨论】:

      【解决方案3】:

      您的问题似乎受到二项式系数的限制。在你给出的例子中,问题可以这样翻译:

      有 6 个项目,一次可以选择 2 个。通过使用二项式系数,可以将唯一组合的总数计算为 N! / (K! (N - K)!,对于 K = 2 的情况,简化为 N(N-1)/2。将 6 代入 N,我们得到 15,这与您计算的组合数量相同6!/ 4!/ 2! - 这似乎是另一种计算二项式系数的方法,这是我以前从未见过的。我也尝试过其他组合,两个公式产生相同数量的组合。所以,它看起来像您的问题可以转化为二项式系数问题。

      鉴于此,您似乎可以利用我编写的一个类来处理处理二项式系数的常用函数:

      1. 以适合任何 N 选择 K 的格式将所有 K 索引输出到文件。 K-indexes 可以替换为更具描述性的字符串或字母。这种方法使得解决这类问题变得非常简单。

      2. 将 K 索引转换为已排序二项式系数表中条目的正确索引。这种技术比依赖迭代的旧已发布技术快得多。它通过使用帕斯卡三角形固有的数学属性来做到这一点。我的论文谈到了这一点。我相信我是第一个发现并发表这种技术的人,但我可能是错的。

      3. 将已排序的二项式系数表中的索引转换为相应的 K 索引。

      4. 使用Mark Dominus方法计算二项式系数,该方法不太可能溢出并且适用于较大的数字。

      5. 该类是用 .NET C# 编写的,并提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用一个名为 InitTable 的 bool 值,当它为 true 时,将创建一个通用列表来保存要管理的对象。如果此值为 false,则不会创建表。无需创建表即可执行上述 4 种方法。提供访问器方法来访问表。

      6. 有一个关联的测试类显示如何使用该类及其方法。它已经过 2 个案例的广泛测试,没有已知的错误。

      要了解该课程并下载代码,请参阅Tablizing The Binomial Coeffieicent

      将此类转换为您选择的语言应该不难。

      可能存在一些限制,因为您使用的 N 非常大,最终可能会创建比程序处理能力更大的数字。如果 K 也可以很大,则尤其如此。目前,该类仅限于 int 的大小。但是,更新它以使用 long 应该不难。

      【讨论】:

        猜你喜欢
        • 2013-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-30
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多