【发布时间】:2012-12-15 01:12:45
【问题描述】:
任何你知道的可能相关的数值方法的方法,请在此处发布!
背景
每个集合都有一个values 数组,每个值的索引对应于该值所绑定到的集合,因此我将集合表示为整数,其中元素表示位位置,例如包含元素一的集合表示为...001,其中1 是LSB。
所以集合只是一个索引,从不存储,它是动态生成的,它是指向数组中表示集合值的索引的键。
我所做的是给定一个集合,是任何大于该集合值的成对不相交子集的总和值。例如。如果集合0111 的值为3,其中两个子集的值为0100 = 2 和0011 = 2,那么这种拆分更有利。我对集合的所有子集都这样做。
给定三个代理,排序是集合数表示。
val[8] = {0,1,2,4,3,2,4,2} the values is not important, only how they are ordered
0 0 0 0 1 1 1 1 MSB bit representation of the index
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 LSB
111 的最佳分割是 011 和 100 的总和为 7。 因此,要获取仅包含第一个元素 ergo 001 的集合的值,请输入 val[1],对于包含元素 1 和 3(101) 的集合,请输入 val[5]。
按基数分组时 val 数组如何排序
val[8] = {0,1,2,3,4,2,4,2}
0 0 0 1 0 1 1 1 MSB bit representation of the index
0 0 1 0 1 0 1 1
0 1 0 0 1 1 0 1 LSB
在这里,您必须将索引转换为数组中正确的 bin,因此对于其中只有第三个元素 (100) val[translate(4)] 的集合看起来像这样。考虑大小> 2 ^ 25个元素的数组。 请查看Improving random memory access when random access is needed 以获得进一步的说明。
但是,这会导致内存中的高阶随机访问,即使我在基数之后对它们进行分组。目前按基数对它们进行分组,生成索引比在集合代表的数字之后排序要慢。
我使用按基数分组的集合生成索引的方法是在常量内存中使用帕斯卡三角形,如Determin the lexicographic distance between two integers 中的答案所述
当它被四个代理按基数排序和分组时集合值所在的位置
n index 1 2 4 8 3 5 6 9 10 12 7 11 13 14 15
-----------------------------------------------------
MSB 0 0 0 1 | 0 0 0 1 1 1 | 0 1 1 1 | 1
0 0 1 0 | 0 1 1 0 0 1 | 1 0 1 1 | 1
0 1 0 0 | 1 0 1 0 1 0 | 1 1 0 1 | 1
LSB 1 0 0 0 | 1 1 0 1 0 0 | 1 1 1 0 | 1
n 索引表示如果没有按基数排序的索引。这只是为了显示每个集合的值所在的位置。
整数集表示值数组中的索引,可以通过直接索引(我目前正在做的,提供随机访问)或通过从集合到索引的转换。
想法
我没有将集合拆分为子集,而是自下而上生成集合。例如。我不会将0111 拆分为所有成对不相交的子集,而是在某个时候从集合{0100,0011},{0010,0101},{0001,0110} 中生成if。
它应该如何工作以及为什么工作
假设我们要评估基数为 3 的集合的所有分裂,因此集合 7,11,13,14。由于拆分基数 3 的集合的唯一方法是拆分成基数 1 和 2 的集合,因此我们需要评估基数 1 和 2 的所有不相交子集的总和是否大于这些集合的并集。
要求的符号(可能有点缺陷):
|C|=n,∀ a,b : a ∪ b = C , a ∩ b ={Ø}, |a|+|b| = n
因此,通过使用对每个线程的合并内存访问读取值,对于形成一组基数 n 的每个子集,检查其值是否大于形成的集合,如果是,则更新该值。
简单的例子,如果n = 2,那么你应该读入所有基数为1的值,并对这些集合进行所有组合并相应地更新。这个例子很简单,因为所有集合都是不相交的:
pseudo code for 4 threads, input card1 is pointer to array of sets |s| =1
__shared__ int value[4];
tid = threadIdx.x;
value[tid] = card1[tid]; // coalesced memory access
int thvalue = value[tid]; // holds the value for the thread, to avoid bank conflict
int rvalue[blockDim.x/2]= 0; //holds the sum
int i = blockDim.x;
int x = 0;
//reduction loop that dont generate duplicate sets
for(;i>0;i>>=1) {
if(tid < i) {
x++;
rvalue[x-1] = value[(tid+x)%blockDim.x] + thvalue;
}
}
for(i = 0; i < x; i++) {
int index = getindex(tid,i,1); //gets the index for the set it generated, 1 represent the cardinality
if(output[index] < rvalue[i])
output[index] = rvalue[i];
}
归约循环的迭代
Thread set specific for thread first iteration second iteration
0 0001 0001 + 0010 0001 + 0100
1 0010 0010 + 0100 0010 + 1000
2 0100 0100 + 1000 none
3 1000 1000 + 0001 none
如您所见,它已获取构成基数 2 集的所有子集的所有值。
然而,问题在于生成大于 2 的基数集更加棘手,因为并非所有集都是不相交的。例如。 0001 和 0011 不是不相交的。
请记住,我不会将集合存储在任何地方,只存储集合的值。
终于
考虑到这一点,您将如何创建一个算法来合并读取内存,并从不相交的子集生成所有集合。在不检查子集是否不相交的情况下,它应该是完全确定的。
赏金
该算法应该是带有标出不同步骤的描述文本,或者是伪代码。
应该用例子证明它是有效的。并不是说这个算法达到了 n^32 个集合,所以它需要很好地扩展。
允许将算法吐出到两个或多个实例,例如一个用于偶数,一个用于奇数。
我很乐意参考有关您使用的技术的来源。
算法应该使用尽可能少的赋值和指令,并且应该避免任何分歧。但是,如果你认为你得到了一个——尽管你有很多这样的东西,试着发帖,我会对任何信息感到满意。
如果以其他方式订购,但它仍然像我描述的那样有效,我强烈建议您在此处发布,任何帮助都非常有帮助
有不清楚的地方请追问。
TL/DR 简单说明
我有一个带有值的数组Z,Z[i] 中的索引i 表示一个整数集,取决于Z 的排序,这些值按基数分组,并按二进制字典排列排序-> 设置值所在的位置 1,2,4,3,5,6,7 索引 4。
通过将集合的值按基数分组,我想要的是查看是否有任何成对的不相交集合值大于它们形成的集合。
例如|a| = 3, |b|+|c| =3, b ∩ c ={Ø}, |b| =1 所以读入X 类型的值的数量b 和X 类型的值的数量来自c,找到b 和c 的所有不相交的子集来自类型a (基数集 3)并得到它们的总和。继续,直到所有集合都“生成”
供参考
【问题讨论】:
-
我可能只是错过了它,但是一组的值是如何确定的呢? (如
0111= 3 的值)。随机访问、基数等都是the other question 的一部分吗? (例如,对于这个问题,您只需要一种算法,该算法假定查找位串值的时间足够短(显然,理想情况下您需要一个解决这两个问题的算法)。 -
@Dukeling 更新了我的文本中的一些内容,但这些值是预先确定的,我所做的只是评估一个集合的任何拆分是否比集合本身更有益。我目前所拥有的工作良好,只是可怕的随机访问。我想要的是一种算法,它可以执行伪代码的功能,但适用于所有基数的集合。因此,一种算法以合并的方式提取与已知基数集相关的值,并计算形成更大基数集的两个集合的所有总和。因此它可以读取基数 1 和 2 的集合以形成基数 3 的集合。
标签: c arrays algorithm cuda dynamic-programming