这是一个简单的算法,可以按字典顺序枚举 [n]={0,...,n-1} 的所有 k 子集。也就是说,这些子集的第一个是 S0=(0,1,2...,k-1),最后一个是 Slast=(n-k, n-k+1,...,n-1)。对于任何 k-子集 S 和任何 0
例如,如果 n=10 且 k=4,则 S0=(0,1,2,3) 且 Slast=(6,7,8,9)。请注意,例如,没有任何组合可以有 S[1]>7(在这种情况下,我们将有 S[j]>n+j-k),因为这样就没有足够的值来填充剩余的位置 j= 2..3.
算法的思想是从第一个组合S0开始,然后反复调用next(),每次生成下一个k-subset。函数 next() 向后遍历当前 k-子集,从最后一个位置 j=k-1 开始向下到 0,直到找到尚未达到其最大允许值 n+j-k 的条目 S[j] 并且可以从而增加。然后它将这个位置加一并用来自 S[j]+1 的连续值填充剩余的位置 j+1..k-1。一旦没有位置可以进一步增加,算法就会停止。
例如,假设我们有 S=(3,7,8,9)。从 j=3 开始,我们看到 S[3],S[2],S[1] 已经达到了它们的最大值。因此,仍然可以增加的最右边的位置是 S[0]。这个值更新为S[0]+1=4,后面的位置更新为5,6,7。因此下一个 k 子集将是 S=(4,5,6,7)。
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
bool next(int *S, int k, int n) {
int j = k-1;
while (j >= 0 && S[j] == n + j - k)
j--;
if (j < 0) return false;
S[j] += 1;
for (int i = j+1; i < k ; i++)
S[i] = S[i-1] + 1;
return true;
}
int main(int argc, char *argv[])
{
int n = 10;
int k = 4;
int *S = (int *)calloc(k, sizeof(int));
for (int j = 0; j < k; S[++j] = j); //first k-subset
int no = 0;
do {
printf("subset #%d: ",no++);
for (int j=0; j < k; j++) {
printf("%d ", S[j]);
}
printf("\n");
} while(next(S, k, n));
return 0;
}