我知道迭代一组大小为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!)还是相同?
答案 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!)
小。