【问题标题】:Generating all subsets using Gosper's Hack (Bankers sequence)使用 Gosper's Hack(银行家序列)生成所有子集
【发布时间】:2016-03-08 10:56:20
【问题描述】:

我有一个方法可以生成数组的所有子集,我想尝试和实现的是同一种方法,但是使用二进制来实现。 Gosper's Hack 似乎是最好的主意,但我不知道如何实现它。下面的代码用于生成所有子集。子集可能是未知的 (http://imgur.com/KXflVjq) 这显示了运行几秒钟后的输出。感谢您的任何建议

int m = prop.length;
int list = (1 << m);
for(long i = 1; i<list; i++) {
   final List sub = new ArrayList<>();
   for(long j=0; j<m; j++) {
      if((i & (1<<j)) > 0) {      
         sub.add(j);
      }
   }
   Collections.sort(sub);
   System.out.println(sub);
}

编辑:由于我的问题措辞不正确,我需要的输出是:

2  1  0
0  0  1 = 0 
0  1  0 = 1

等等

【问题讨论】:

  • 你的问题是什么?生成所有子集的速度很慢吗?
  • 不管怎样,Collections.sort(sub) 在这里是不必要的,因为项目被添加到sub 中是为了在初始数组中存在,所以排序是浪费时间。
  • 好吧,太棒了,我把那一点去掉。是的,因为程序需要尽可能快,并且以这种方式生成它们并不是最佳的。
  • (en.wikipedia.org/wiki/Combinatorial_number_system#Applications) 这是我找到的唯一描述它但很难理解的方法
  • 如果您需要生成 所有可能 子集,没有什么比这更快了,因为子集的数量成倍地取决于输入大小(例如,对于 30 个元素,存在约 10 亿个子集,对于 40 个元素 - 10^12 等)

标签: java subset


【解决方案1】:

首先,我想指出的是,尚不清楚您想要实现的具体目标是什么。请考虑澄清问题。我假设您想要生成一个 n-set 的所有 k-subsets。该问题可以很容易地简化为生成所有 {1,2,...,n}k 子集(即,计算所有 k-索引子集)。

一种生成n-集合的k-子集的算法

不久前,我写了this 一个方法的实现(几年前我重新发现了它),用于生成 n 集的所有 k-子集。希望能帮助到你。该算法本质上以巧妙的方式访问了所有长度为 n 的二进制序列,其中恰好包含 k 个序列(无需遍历所有 2^n 序列);请参阅描述算法的the accompanying note,其中包含详细描述、伪代码和一个小的分步示例。

我认为时间复杂度的顺序是 O(k {n choose k})。我还没有正式的证明。 (很明显,任何算法都必须花费 Omega({n choose k}) 时间。)

C 中的代码:

#include <stdlib.h>
#include <stdio.h>

void subs(int n, int k);

int main(int argc, char **argv)
{
    if(argc != 3) return 1;
    int n, k;

    n = atoi(argv[1]); k = atoi(argv[2]);
    subs(n, k);

    return 0;
}

void subs(int n, int k)
{
    int *p = (int *)malloc(sizeof(int)*k);
    int i, j, r;

    for(i = 0; i < k; ++i) p[i] = i; // initialize our ``set'' 
    // the algorithm
    while(1)
     {  // visit the current k-subset
        for(i = 0; i < k; ++i)
            printf("%d ", p[i]+1);
        printf("\n");

        if(p[0] == n-k) break; // if this is the last k-subset, we are done 

        for(i = k-1; i >= 0 && p[i]+k-i == n; --i); // find the right element 
        r = p[i]; ++p[i]; j = 2; // exchange them 
        for(++i; i < k; ++i, ++j) p[i] = r+j; // move them 
     }
    free(p);
}

参考文献

如果这还不够有效,我强烈推荐 Knuth 的 Volume 4 of The Art of Comouter Programming,他在那里广泛地处理了这个问题。这可能是目前最好的参考资料(而且是最近的!)。

您甚至可以找到分册草稿,TAOCP 第 4 卷分册 3,生成所有组合和分区 (2005),vi+150pp。 ISBN 0-201-85394-9,在 Knuth 的主页上(参见他 2011 年左右的新闻)。

【讨论】:

  • 感谢您的回答,真的很有帮助!我去看看书!
  • @blazs 我试图了解该算法的工作原理,但无法完全干运行它。你介意展示它是如何与这个例子一起工作的吗?编码流程。
  • 我在帖子中链接的 PDF 中有一个示例。如果这对您不起作用,请告诉我——我可以准备一个更详细的示例。
  • @blazs 谢谢。奇怪的是,我以某种方式理解了算法,但无法遵循代码是如何完成的。例如,在代码中找到正确元素的行。我尝试用示例干运行它,但无法遵循。如果您能通过示例帮助我理解代码流,那将是非常棒的。再次感谢这个答案。我发现最好的学习 gospers hack。
猜你喜欢
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
  • 2020-02-18
  • 2023-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多