【发布时间】:2013-12-17 12:54:00
【问题描述】:
我在 C 语言中有一个相对较短的数组(= 2 的所有可能子集。通过递归构建所有子列表的列表,有很多方法可以做到这一点,但我想避免这增加的额外开销。我只需要遍历每个子集;我不需要跟踪它们。
这听起来像是一个奇怪的要求,但原因是我还希望能够在每个工作项内存非常昂贵的 OpenCL 内核中使用它。我真的很想避免分配列表列表。
【问题讨论】:
标签: c arrays opencl bit-manipulation subset
我在 C 语言中有一个相对较短的数组(= 2 的所有可能子集。通过递归构建所有子列表的列表,有很多方法可以做到这一点,但我想避免这增加的额外开销。我只需要遍历每个子集;我不需要跟踪它们。
这听起来像是一个奇怪的要求,但原因是我还希望能够在每个工作项内存非常昂贵的 OpenCL 内核中使用它。我真的很想避免分配列表列表。
【问题讨论】:
标签: c arrays opencl bit-manipulation subset
如果您包含大小为 0 和 1 的子集并过滤掉它们(这很简单,if ((set & (set - 1)) == 0),忽略它),您实际上只是从 0 迭代到 1 << n。
这比 Gosper's Hack 简单得多,这很酷,但由于您基本上需要所有子集长度,因此使用它没有什么意义。只有几个子集以这种方式被跳过,因为您不想要的唯一重要组只有大小 n。
【讨论】:
鉴于数组非常短,我们可以在 32 位无符号整数上使用一些位摆弄来完成此操作。如果数组的每个元素都表示为位串中的单个位,并且该位的值指定该元素是否在当前子集中,则问题简化为查找所有具有特定长度的位串,其中没有位设置超过kth 位位置,其中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;
}
}
【讨论】:
您需要的是一种算法,该算法可以在给定工作项 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 输出大小。
【讨论】: