11

关于您知道的任何可能相关的数值方法的任何方法,请在此处发布!

背景

我对每个集合都有一个数组,values每个值的索引对应于该值所绑定的集合,因此我将一个集合表示为一个整数,其中元素表示位位置,例如其中包含元素一的集合是表示为. ...001_1LSB

所以集合只是一个索引,从不存储,它是动态生成的,它是指向数组中表示集合值的索引的键。

我所做的是给定一个集合,是任何成对不相交子集的总和值大于该集合的值。例如,如果 set0111的值为 3,其中两个子集的值为0100 = 20011 = 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,读取Xtype 的值的数量和 type 的值的b数量,找到Xtypec的所有不相交的子集(基数集 3)并得到它们的总和。继续,直到所有集合都“生成”bca

以供参考

基于汉明权重的索引

确定两个整数之间的字典距离

在需要随机访问时改进随机内存访问

4

2 回答 2

1

我不知道这是否对您有帮助,但我在Hacker's Delight中发现了一个无分支的 count-all-the-1-bits-in-a-word 函数,它似乎有助于您确定基数一组:

int pop(unsigned int x) {
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

在正文中,Warren 声称上述序列可以编译成少至 21 条指令。然而,在 i7 开发机器上使用 MSVC 2010,我检查了这个函数的反汇编,发现它在大约 22 条指令中进行实际计算,总共有 33 条指令(计算堆栈操作)。在现代 CPU 或 GPU 上,它应该非常快,因为它没有分支。

于 2013-01-05T21:35:15.713 回答
1

尝试利用三元编号

对于术语,我称“值”您设置的估值函数和“目标”您的目标函数,它是每个二进制分区上值总和的最大值。

二进制数 B 每次分裂成两个不相交的部分 L 和 R,都可以用三进制数 C 表示,其中

B = L | R   (bitwise OR)
L ^ R = 0   (bitwise XOR)

C[i] = 0 means B[i] = 0 and L[i] = R[i] = 0
C[i] = 1 means B[i] = 1 and L[i] = 1
C[i] = 2 means B[i] = 2 and R[i] = 1

然后“简单”地枚举从 1 到 3**n 的三进制数:例如 (n=3): 000, 001, 002, 010, 011, 012, 020, ...

好的,实际上,当您手头只有二进制时,有效地计算三进制并不是一件容易的事。但请耐心等待,我将在完成高级算法后解释这一点......

所以你按三进制计算,给定一个三进制数 C,你得到 L 和 R - 如何?我也会在下面解释,相信我:)

给定 L 和 R,您现在可以在 L 和 R 处查找您的估值并在 B 处更新目标:target[B] = max(val[L], val[R])。

好的,这就是高级算法。我无法在这么短的时间内证明这一点,但它似乎确实具有非常好的缓存位置属性。换句话说,value[L] 和 value[R] 将倾向于一次停留在少量缓存行中。此外,我认为并行化的最佳选择是拆分i为模 3 的值或模 9 的值等。

二进制中的有效三进制计数

我们如何有效地计算三元?尝试以下方法:以 4 为底数,然后跳过一些。

换句话说,一个三进制数字将由两位表示,我们将禁止这种组合11

 repr | value
 0 0  | 0
 0 1  | 1
 1 0  | 2
 1 1  | *undefined*

现在,我们如何有效地知道何时跳过?好吧,增量模式很容易弄清楚:

1 1 2 1 1 2 1 1 6 1 1 2 1 1 2 1 1 6 1 1 2 1 1 2 1 1 22 1 1 2 ...

我的建议是预先计算一大块大小为 3 的幂(例如 3 ** 7 = 2187)并偶尔计算 3 的 n 次方 [提示:它与 n 的立方体有关 ..] .

所以你从 00.00.00 开始。您添加 1 即 00.00.01。您添加 1 即 00.00.10。现在您必须添加 2 才能跳过 11 组合,剩下的就是 00.01.00。等等。

如何从 C 中获得 L 和 R

现在,在我们的四进制表示中的 C 实际上只是 L 和 R 交错的。为了有效地恢复 L 和 R,您可以检查此 S/O 问题的答案或应用其他一些小技巧。

事后诸葛亮

总而言之,我不确定我们是否真的使用了 base 3 或 base 4。哦,好吧......

玩得开心,祝你好运!

于 2013-01-10T01:43:27.833 回答