我有N个点,它们仅位于维度D的多维数据集的顶点上,其中D等于3。
顶点可能不包含任何点。所以,每一个点在{0,1}坐标d。我只对 查询时间 感兴趣,只要内存成本是合理的(例如,在N中不是指数):)。
给定位于多维数据集的一个顶点和一个输入参数上的查询,请在查询中r找到 汉明距离 <=的所有顶点(因此是点)r。
r
在c ++环境中应该采取什么方式?
我正在考虑使用kd树,但是我不确定并且需要帮助,任何输入,甚至是近似输入,都将不胜感激!由于 汉明距离 起作用,因此按位操作应有所帮助(例如XOR)。
从一个k设置了位的位掩码到按字典顺序排列的下一个排列是一个不错的bithack ,这意味着遍历所有k设置了位的掩码非常简单。将这些蒙版与初始值进行XOR运算,可以得到所有汉明距离都与汉明正好相反的值k。
k
因此,对于小于32的D尺寸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)
nextBitPermutation
__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效果很好,并且通常所有内容都排列良好,如下所示:
popcnt
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中成指数地变慢。