汉明立方体顶点上的查询点

时间:2016-11-23 15:37:08

标签: c++ algorithm data-structures computational-geometry hamming-distance

我有N个点只位于立方体的顶点上,尺寸为D,其中D就像是3。

顶点可能不包含任何点。所以每个点的坐标都在{0,1} D 中。我只对查询时间感兴趣,只要内存成本合理(例如在N中不是指数):)。

如果查询位于其中一个立方体的顶点和输入参数r,请找到所有具有汉明距离的顶点(因此点)<=查询r

环境中的方法是什么?

我正在考虑一棵kd树,但我不确定并且想要帮助,任何输入,甚至是近似的,都会受到赞赏!由于汉明距离发挥作用,按位操作应该有所帮助(例如XOR)。

1 个答案:

答案 0 :(得分:4)

k位设置为lexicographically next permutation的一个位掩码可以获得很好的结果,这意味着使用k位循环遍历所有掩码非常简单组。使用初始值对这些蒙版进行异或运算会使汉明距离的所有值与k完全相同。

因此对于D维度,其中D小于32(否则更改类型),

uint32_t limit = (1u << D) - 1;
for (int k = 1; k <= r; k++) {
    uint32_t diff = (1u << k) - 1;
    while (diff <= limit) {
        // v is the input vertex
        uint32_t vertex = v ^ diff;
        // use it
        diff = nextBitPermutation(diff);
    }
}

nextBitPermutation可以在C ++中实现(如果你有__builtin_ctz

uint32_t nextBitPermutation(uint32_t v) {
    // see https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
    uint32_t t = v | (v - 1);
    return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
}

或MSVC(未测试)

uint32_t nextBitPermutation(uint32_t v) {
    // see https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
    uint32_t t = v | (v - 1);
    unsigned long tzc;
    _BitScanForward(&tzc, v); // v != 0 so the return value doesn't matter
    return (t + 1) | (((~t & -~t) - 1) >> (tzc + 1));
}

如果D 低,4或更低,旧的popcnt - 带 - pshufb效果非常好,通常所有内容都排好,如下所示:< / p>

uint16_t query(int vertex, int r, int8_t* validmask)
{
    // validmask should be array of 16 int8_t's,
    // 0 for a vertex that doesn't exist, -1 if it does
    __m128i valid = _mm_loadu_si128((__m128i*)validmask);
    __m128i t0 = _mm_set1_epi8(vertex);
    __m128i r0 = _mm_set1_epi8(r + 1);
    __m128i all =        _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
    __m128i popcnt_lut = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2,  2,  3,  2,  3,  3,  4);
    __m128i dist = _mm_shuffle_epi8(popcnt_lut, _mm_xor_si128(t0, all));
    __m128i close_enough = _mm_cmpgt_epi8(r0, dist);
    __m128i result = _mm_and_si128(close_enough, valid);
    return _mm_movemask_epi8(result);
}

这应该相当快;与上面的bithack相比较快(nextBitPermutation,相当重,在那里使用很多)并且还比较了所有顶点的循环并测试它们是否在范围内(即使使用内置popcnt,假设所有内容都被高速缓存或甚至永久保存在寄存器中,则自动至少需要16个周期而且上面不应该这样做。缺点是结果令人烦恼,因为它是一个掩模,其中顶点都存在并且在查询点的范围内,而不是它们的列表。它可以很好地与对点相关的数据进行一些处理。

当然,这也可以缩小到D = 3,只是没有任何一个点&gt; = 8有效。 D&gt; 4可以类似地完成,但它需要更多的代码,因为这实际上是一个蛮力解决方案,由于并行性而很快,它在D中从根本上呈指数级下降。