小编典典

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

algorithm

我有N个点,它们仅位于维度D的多维数据集的顶点上,其中D等于3。

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

给定位于多维数据集的一个顶点和一个输入参数上的查询,请在查询中r找到 汉明距离 <=的所有顶点(因此是点)r

c ++环境中应该采取什么方式?


我正在考虑使用kd树,但是我不确定并且需要帮助,任何输入,甚至是近似输入,都将不胜感激!由于 汉明距离 起作用,因此按位操作应有所帮助(例如XOR)。


阅读 332

收藏
2020-07-28

共1个答案

小编典典

从一个k设置了位的位掩码到按字典顺序排列下一个排列是一个不错的bithack
,这意味着遍历所有k设置了位的掩码非常简单。将这些蒙版与初始值进行XOR运算,可以得到所有汉明距离都与汉明正好相反的值k

因此,对于小于32的D尺寸D(否则请更改类型),

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 真的 很低,popcnt等于或小于4,那么旧的withwith pshufb效果很好,并且通常所有内容都排列良好,如下所示:

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,仅使> = 8的点都不有效。D>
4可以类似地完成,但是要花更多的代码,并且由于这实际上是蛮力解决方案,仅由于并行性而很快,因此从根本上讲,它在D中成指数地变慢。

2020-07-28