使用Gosper&#Hack(银行家序列)生成所有子集

时间:2016-03-08 10:56:21

标签: java subset

我有一个生成数组的所有子集的方法,我想尝试和实现的方法是相同的方法,但使用二进制方法。 Gosper的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

1 个答案:

答案 0 :(得分:2)

首先,我要注意它不清楚你究竟想要达到的目的是什么;请考虑澄清问题。我假设您要生成 n -set的所有 k 子集。这个问题可以很容易地减少到生成 {1,2,...,n} 的所有 k 子集的问题(即它足以计算所有 k -subsets of indices)。

用于生成 n -set

k 子集的算法

前段时间我写了一个方法(我几年前重新发现)的this实现,用于生成 n -set的所有 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。国际标准书号0-201-85394-9,在Knuth的主页上(见他2011年左右的新闻)。