C++ Hamming立方体顶点上的查询点

C++ Hamming立方体顶点上的查询点,c++,algorithm,data-structures,computational-geometry,hamming-distance,C++,Algorithm,Data Structures,Computational Geometry,Hamming Distance,我有N个点,它们只位于立方体的顶点上,维度为D,其中D类似于3 顶点不能包含任何点。所以每个点都有{0,1}D中的坐标。我只对查询时间感兴趣,只要内存开销合理(例如,不是N中的指数:) 给定一个位于立方体顶点之一的查询和一个输入参数r,找到所有具有汉明距离的顶点(即点),从设置了k位的一个位掩码到,这意味着在设置了k位的所有掩码之间循环非常简单。用一个初始值对这些掩码进行XORing运算,可以得到距离它k精确的所有汉明距离值 因此对于D维度,其中D小于32(否则更改类型) 或用于MSVC(未测试

我有N个点,它们只位于立方体的顶点上,维度为D,其中D类似于3

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


给定一个位于立方体顶点之一的查询和一个输入参数
r
,找到所有具有汉明距离的顶点(即点),从设置了
k
位的一个位掩码到,这意味着在设置了
k
位的所有掩码之间循环非常简单。用一个初始值对这些掩码进行XORing运算,可以得到距离它
k
精确的所有汉明距离值

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

或用于MSVC(未测试)


如果D真的很低,4或更低,旧的
popcnt
-with-
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中基本上会以指数方式变慢。

从一个位掩码开始,使用
k
位有一个很好的位破解s设置为,这意味着通过设置了
k
位的所有掩码进行循环非常简单。将这些掩码与初始值进行XOR运算,将给出距离它正
k
处的所有值

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

或用于MSVC(未测试)


如果D真的很低,4或更低,旧的
popcnt
-with-
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中它基本上会以指数形式变慢。

向下投票,为什么?:/Downvoter先生,请建议我如何改进我的qu估计-你甚至没有投反对票来帮助我…!不是我的-1,但正如我所理解的“维度诅咒”,它确切地意味着对于D>>1,没有人知道解决这个问题的好方法。随着D的增加,最愚蠢的算法(检查你的N个点,看看它是否在查询点的r范围内)很快就成为了最著名的算法。@j_random_hacker,作为其中的一个创造者,我可以说我们在这里不必对维度诅咒做那么多,我的意思是D在我的情况下永远不会是10.000,但感谢你的提示,我会解决它的!)使用一些bitmath技巧,您可以简单地枚举距离正好
k
的所有顶点,然后将
k
循环到
r
。我以前已经回答过这个问题,让我去找它。。是的@harold,您同时评论说按位操作应该有帮助,我正在寻找类似的方法。太好了,我会等的!下一票,为什么?:/Downvoter先生,请建议我如何改进我的问题-你甚至没有投下下下一票来帮助我…!不是我的-1,但正如我理解的“维度诅咒”,它确切地意味着对于D>>1,没有人知道解决这个问题的好方法。随着D的增加,这是最愚蠢的算法(检查你的N个点,看看它是否在查询点的r范围内)很快成为最著名的算法。@j_random_hacker,作为创建这个算法的人之一,我可以说我们不必在这里对维度诅咒做那么多,我的意思是,在我的情况下,D永远不会是10.000,但感谢你的提示,我会修复它!)使用一些bitmath技巧,您可以简单地枚举距离正好
k
的所有顶点,然后将
k
循环到
r
。我以前已经回答过这个问题,让我去找它。。是的@harold,您同时评论说按位操作应该有帮助,我正在寻找类似的方法。太好了,我会等的!我可以使用
unit8\u t
,因为D不会超过这个,对吗?但这里的问题是我不明白哪个是查询,是
v
?我的观点在哪里?一些(很多?)立方体的顶点可能为空,因此并非所有排列都指向点。@gsamaras是的,
v
。是的,如果N为空,这就不再是一个好主意
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));
}
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);
}