考虑2D,3D,(4D ......)空间中的点阵列(例如unstructured mesh的节点)。最初,数组中点的索引与其在空间中的位置无关。在简单的情况下,假设我已经知道一些最近邻连接图。
我想要一些启发式方法来增加在空间中彼此接近的两个点具有相似索引的概率(将在阵列中接近)。
我知道确切的解决方案非常困难(可能类似于Travelling salesman problem)但我不需要精确解决方案,只需要增加概率。
我对解决方案的看法:
一些天真的解决方案就像:
1. for each point "i" compute fitness E_i given by sum of distances in array (i.e. index-wise) from its spatial neighbors (i.e. space-wise)
E_i = -Sum_k ( abs( index(i)-index(k) ) )
where "k" are spatial nearest neighbors of "i"
2. for pairs of points (i,j) which have low fitness (E_i,E_j)
try to swap them,
if fitness improves, accept
但详细的实施及其性能优化并不是那么清楚。
其他不需要预先计算的最近邻居的解决方案将基于某些Locality-sensitive_hashing
我认为这个可能是一个非常普遍的问题,并且可能存在很好的解决方案,我不想重新发明轮子。
申请
答案 0 :(得分:2)
我会说space filling curves(SPC)是将空间接近度映射到线性排序的标准解决方案。最常见的是Hilbert-curves和z-curves (Morton order)。
希尔伯特曲线具有最佳的邻近映射,但计算起来有些昂贵。 Z排序仍然具有良好的邻近映射,但是非常容易计算。对于z排序,交织每个维度的比特就足够了。假设整数值,如果你有一个64位的3D点(x,y,z),则z值为$ x_0,y_0,z_0,x_1,y_1,z_1,... x_63,y_63,z_63 $,即a 192位值由每个维度的第一位组成,后跟每个维度的第二位,依此类推。如果您的数组是根据该z值排序的,则空间中接近的点通常也会在数组中关闭。
Here是将(merge
)值交织为z值(nBitsPerValue
通常为32或64)的示例函数:
public static long[] mergeLong(final int nBitsPerValue, long[] src) {
final int DIM = src.length;
int intArrayLen = (src.length*nBitsPerValue+63) >>> 6;
long[] trg = new long[intArrayLen];
long maskSrc = 1L << (nBitsPerValue-1);
long maskTrg = 0x8000000000000000L;
int srcPos = 0;
int trgPos = 0;
for (int j = 0; j < nBitsPerValue*DIM; j++) {
if ((src[srcPos] & maskSrc) != 0) {
trg[trgPos] |= maskTrg;
} else {
trg[trgPos] &= ~maskTrg;
}
maskTrg >>>= 1;
if (maskTrg == 0) {
maskTrg = 0x8000000000000000L;
trgPos++;
}
if (++srcPos == DIM) {
srcPos = 0;
maskSrc >>>= 1;
}
}
return trg;
}
您还可以交错浮点值的位(如果使用IEEE 754进行编码,就像它们通常在标准计算机中一样),但这会导致非欧几里德距离属性。您可能必须先转换负值,请参阅here,第2.3节。
修改强> 两个回答评论中的问题:
1)我理解如何制作常规的空间填充曲线 矩形网格。但是,如果我随机定位漂浮 点,几个点可以映射成一个盒子。该算法会起作用吗? 在那种情况下?
有几种方法可以使用浮点(FP)值。最简单的方法是将它们乘以一个大常数,将它们转换为整数值。例如,将所有内容乘以10 ^ 6以保持6位精度。
另一种方法是使用FP值的位级表示将其转换为整数。这样做的好处是不会丢失精度,也不必确定乘法常数。缺点是欧几里德距离度量不再起作用。
它的工作原理如下:技巧是浮点值不具有无限精度,但限制为64位。因此它们自动形成网格。与整数值的差异在于浮点值不形成二次网格,而是矩形网格,其中矩形随着距离(0,0)的增长而变大。网格大小取决于给定点的可用精度。接近(0,0),精度(= grid_size)为10 ^ -28,接近(1,1),它是10 ^ -16见here。这个失真的网格仍然具有邻近映射,但距离不再是欧几里德。
以下是进行转换的代码(Java,取自here;在C ++中,您只需将float
转换为int
):
public static long toSortableLong(double value) {
long r = Double.doubleToRawLongBits(value);
return (r >= 0) ? r : r ^ 0x7FFFFFFFFFFFFFFFL;
}
public static double toDouble(long value) {
return Double.longBitsToDouble(value >= 0.0 ? value : value ^ 0x7FFFFFFFFFFFFFFFL);
}
这些转换保留了转换值的排序,即对于每两个FP值,得到的整数具有与&lt;,&gt;,=相同的排序。非欧几里德行为是由在位串中编码的指数引起的。如上所述,我们还讨论了here,第2.3节,但代码稍微不那么优化了。
2)是否有一些算法如何对这样的空间进行迭代更新 如果我的点在太空中移动,填充曲线? (即没有重新排序 每次整个阵列)
空间填充曲线强加了特定的排序,因此对于每组点,只有一个有效排序。如果移动一个点,则必须将其重新插入由其z值确定的新位置。
好消息是小动作可能意味着一个点可能经常停留在阵列的同一“区域”。因此,如果您真的使用固定数组,则只需移动它的一小部分。
如果你有很多移动物体并且阵列很麻烦,你可能想要查看“移动物体索引”(MX-CIF-quadtree等)。我个人可以推荐我自己的PH-Tree。它是一种按位基数四叉树,使用z曲线进行内部排序。它对于更新(和其他操作)非常有效。但是,我通常建议它只用于较大的数据集,对于小数据集,简单的四叉树通常就足够了。
答案 1 :(得分:1)
你试图解决的问题有意义iff,给定点p及其NN q,那么q的NN确实是p。
那是不琐碎,因为例如两个点可以代表景观中的位置,所以一个点在山中可能很高,所以从底部到山的成本更高反过来(从山到山)。所以,请确保检查不是你的情况。
由于TilmannZ已经提出了一个解决方案,我想强调你提到的LSH。我会不选择那个,因为你的点位于一个非常低维空间,它甚至不是100,所以为什么要使用LSH?
我会针对该案例采用CGAL的算法,例如2D NNS,甚至是简单的kd-tree。如果速度是关键的,但空间不是,那么为什么不选择quadtree(3D中的八叉树)?我已经构建了一个,在8GB RAM中不会超过10个维度。
但是,如果您认为您的数据将来可能属于更高维度的空间,那么我建议您使用: