关于您知道的任何可能相关的数值方法的任何方法,请在此处发布!
背景
我对每个集合都有一个数组,values
每个值的索引对应于该值所绑定的集合,因此我将一个集合表示为一个整数,其中元素表示位位置,例如其中包含元素一的集合是表示为. ...001
_1
LSB
所以集合只是一个索引,从不存储,它是动态生成的,它是指向数组中表示集合值的索引的键。
我所做的是给定一个集合,是任何成对不相交子集的总和值大于该集合的值。例如,如果 set0111
的值为 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。因此,要获得仅包含第一个元素的集合的值,即 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个元素的数组。在需要随机访问时查看改进随机内存访问以进行进一步说明。
但是,这会导致内存中的高阶随机访问,即使我在基数之后对它们进行分组。目前按基数对它们进行分组,生成索引比在集合代表的数字之后排序它们要慢。
我使用按基数分组的集合生成索引的方法是在常量内存中使用帕斯卡三角形,如确定两个整数之间的字典距离中的答案所述
当集合值按基数与四个代理进行排序和分组时所在的位置
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 index 表示如果未按基数排序时它的索引。这只是为了显示每个集合的值所在的位置。
整数集表示值数组中的索引,可以通过直接索引(我目前正在做的,提供随机访问)或通过从集合到索引的转换。
这个想法
我没有将集合拆分为子集,而是自下而上生成集合。例如,我不会拆分0111
到所有成对的不相交子集,而是在某个时候从集合中生成 if {0100,0011},{0010,0101},{0001,0110}
。
它应该如何以及为什么起作用
假设我们要评估基数为 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
我有一个带有值的数组,其中i
的索引Z[i]
表示一个整数集,具体取决于 的顺序Z
,这些值按基数分组,并按二进制字典排列排序-> 集合值位于 1、2、4 的位置, 3,5,6,7 <- 所以我使用一个函数(我实现了这个函数)将索引转换为正确的索引。例如设置 3-> 索引 4。
通过将集合的值按基数分组,我想要的是查看是否有任何成对的不相交集合值大于它们形成的集合。
例如|a| = 3, |b|+|c| =3, b ∩ c ={Ø}, |b| =1
,读取X
type 的值的数量和 type 的值的b
数量,找到X
typec
的所有不相交的子集(基数集 3)并得到它们的总和。继续,直到所有集合都“生成”b
c
a