我在C中有一个相对较短的数组(< 32个元素),并希望遍历该长度为> = 2的数组的所有可能子集。通过递归构建所有列表,有很多方法可以做到这一点。子列表,但我想避免增加额外的开销。我只需要迭代每个子集;我不需要跟踪它们。
这可能听起来像一个奇怪的要求,但原因是我也希望能够在OpenCL内核中使用它,其中每个工作项内存非常昂贵。分配列表是我真正想避免的。
答案 0 :(得分:2)
如果你包含大小为0和1的子集,只是将它们过滤掉(这很简单,
if ((set & (set - 1)) == 0)
,忽略它,你真的只是从0迭代到1 << n
。
这比Gosper的Hack简单得多,这很酷,但是因为你需要基本上所有的子集长度,所以使用它没什么意义。只有几个子集以这种方式被跳过,因为它们中唯一不需要的非平凡组只有n
的大小。
答案 1 :(得分:1)
鉴于数组很短,我们可以在32位unsigned int上使用一些bit-fiddling来实现这一点。如果数组的每个元素在bitstring中表示为单个位,并且该位的值指定该元素是否在当前子集中,那么问题将减少到找到某些长度的所有位串,其中没有位是设置超过k
位的位置,其中k
是数组的长度。
/*
Given:
max = longest subset length
min = shortest subset length
num = number of elements in array
*/
unsigned int i, n, v, w;
// Loop across subset lengths
for (n = max; n >= min; n--) {
// Generate lexiographically first subset (n rightmost bits set)
v = (~0U)>>(sizeof(unsigned int)*8-n);
// Stop once a bit is set that is outside our array
while (v < (1U<<num)) {
// Look for elements whose corrensponding bit is set
for (i = 0; i < num; i++) {
if (v & (1U<<i)) {
// array[i] is in current subset
}
}
// Move to lexiographically next bit string with n bits set
// http://www-graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
// Could switch to __builtin_ctz() version for speed
w = (v | (v - 1)) + 1;
w |= ((((w & -w) / (v & -v)) >> 1) - 1);
v = w;
}
}
答案 2 :(得分:0)
您需要的是一种算法,可以根据工作项ID和数组长度为您提供排列或组合序列。例如:
Array: 1 2 3
Permutations:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
workitem i=5 -> N=3, K=6; *Some algorithm that gives 312*
我猜这些算法存在,你甚至可以编写自己的应用逻辑。 (我会稍后尝试挖掘它们,但搜索“并行置换/组合”)
然后,您只需要编写一个通用内核来运行算法,全局大小的数量等于可能的组合数量。我会为数组值使用常量内存(因为它的大小很小)。 对于所有输出值,您还需要一个BIG输出大小。