【问题标题】:Bottom up set generation and ordering自下而上的集合生成和排序
【发布时间】:2012-12-15 01:12:45
【问题描述】:

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

背景

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

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

我所做的是给定一个集合,是任何大于该集合值的成对不相交子集的总和值。例如。如果集合0111 的值为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。 因此,要获取仅包含第一个元素 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 简单说明

我有一个带有值的数组ZZ[i] 中的索引i 表示一个整数集,取决于Z 的排序,这些值按基数分组,并按二进制字典排列排序-> 设置值所在的位置 1,2,4,3,5,6,7 索引 4。

通过将集合的值按基数分组,我想要的是查看是否有任何成对的不相交集合值大于它们形成的集合。

例如|a| = 3, |b|+|c| =3, b ∩ c ={Ø}, |b| =1 所以读入X 类型的值的数量bX 类型的值的数量来自c,找到bc 的所有不相交的子集来自类型a (基数集 3)并得到它们的总和。继续,直到所有集合都“生成”

供参考

Hamming weight based indexing

Determin the lexicographic distance between two integers

Improving random memory access when random access is needed

【问题讨论】:

  • 我可能只是错过了它,但是一组的值是如何确定的呢? (如0111 = 3 的值)。随机访问、基数等都是the other question 的一部分吗? (例如,对于这个问题,您只需要一种算法,该算法假定查找位串值的时间足够短(显然,理想情况下您需要一个解决这两个问题的算法)。
  • @Dukeling 更新了我的文本中的一些内容,但这些值是预先确定的,我所做的只是评估一个集合的任何拆分是否比集合本身更有益。我目前所拥有的工作良好,只是可怕的随机访问。我想要的是一种算法,它可以执行伪代码的功能,但适用于所有基数的集合。因此,一种算法以合并的方式提取与已知基数集相关的值,并计算形成更大基数集的两个集合的所有总和。因此它可以读取基数 1 和 2 的集合以形成基数 3 的集合。

标签: c arrays algorithm cuda dynamic-programming


【解决方案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 上,它应该非常快,因为它没有分支。

【讨论】:

  • CUDA 有一个用于人口计数的 __popc 指令,我正在寻找一种算法,它可以进行自下而上的集合生成,对生成集合的子集的值进行评估。但感谢您的考虑。
【解决方案2】:

尝试利用三元编号

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

将二进制数 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,您可以查看this S/O question 的答案或应用其他一些小技巧。

事后考虑

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

玩得开心,祝你好运!

【讨论】:

  • 顺便说一下,如果你需要高效的模 3,你可以使用 2 mod 3 == -1 mod 3 这个事实,所以配方是:将你的输入数字去交错成偶数和奇数位,并减去位人口计数
猜你喜欢
  • 2016-02-14
  • 2016-09-19
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多