在C中查找短数组的所有可能子集

时间:2013-12-17 12:54:00

标签: c arrays opencl bit-manipulation subset

我在C中有一个相对较短的数组(< 32个元素),并希望遍历该长度为> = 2的数组的所有可能子集。通过递归构建所有列表,有很多方法可以做到这一点。子列表,但我想避免增加额外的开销。我只需要迭代每个子集;我不需要跟踪它们。

这可能听起来像一个奇怪的要求,但原因是我也希望能够在OpenCL内核中使用它,其中每个工作项内存非常昂贵。分配列表是我真正想避免的。

3 个答案:

答案 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输出大小。