迭代给定大小的所有子集

时间:2013-04-10 17:12:20

标签: performance combinatorics

我知道迭代一组大小为n的所有子集是性能噩梦,需要花费O(2 ^ n)时间。

如何迭代大小为k的所有子集(对于(0 <= k <= n))?这是一场表演噩梦吗?我知道有(n,k)= n! / k! (n - k)!可能性。我知道如果k非常接近0或非常接近n,这是一个很小的数字。

n和k方面的最差情况是什么?除了O(n!/ k!(n - k)!)之外,还有一种更简单的方式吗?这是渐近地小于O(n!)还是相同?

2 个答案:

答案 0 :(得分:8)

你想要Gosper的黑客:

int c = (1<<k)-1;
while (c < (1<<n)) {
  dostuff(c);
  int a = c&-c, b = c+a;
  c = (c^b)/4/a|b;
}

说明:

使用尽可能多的位设置查找下一个数字基本上减少到只有一个“块1”的数字的情况---数字有一堆零,然后是一堆1,然后是一堆零再次他们的二进制扩展。

处理这样一个“一块”数字的方法是将最高位向左移动并将所有其他位置尽可能低。 Gosper的黑客工作是找到最低设置位(a),找到包含我们不触摸的位的“高位”和“进位”(b),然后产生一个块从最不重要的位开始的适当大小。

答案 1 :(得分:0)

很容易证明,对于固定的n(n, k)的最大值为k = n/2。如果我没有误用英镑的近似值,(n, n/2)的渐近行为是指数的。

对于常数k(n, k)O(n^k)。请记住,组合函数是对称的,因此对(n, n-k)来说是相同的。它是多项式的,因此它比O(n!)小。