多段线数组中最邻近多段线快速查找的算法优化方案问询(支持前Q个结果)
多段线数组中最邻近多段线快速查找的算法优化方案问询(支持前Q个结果)
场景与现有实现
我最近在处理一个多段线近邻查找的需求,目前的线性搜索方案在大数据集下实在太慢,想请教各位有没有更高效的优化思路。先跟大家说下我的场景:
我需要维护一个数据集,里面全是等长度的多段线(每条线由固定数量的3D点组成),点的顺序是严格对应的——计算两条线的距离时,是把对应位置点的曼哈顿距离累加起来。现在要做的是,给任意一条目标多段线,快速找到数据集中和它最近的那条;另外后续还需要支持返回前Q个最近的线。
目前我用的是最直接的线性搜索实现findClosest方法,小数据集跑起来没问题,但一旦数据集到几十万甚至近百万条线,每次查找的耗时就完全没法接受了。我也想过用二分查找,但实在找不到合适的规则给这些多段线排序,这条路走不通。
之前了解过k-d树这种空间索引结构,知道它适合单点的近邻查找,但完全摸不着头脑怎么把它扩展到这种由多个点组成的多段线场景。
下面是我目前的Java实现代码,方便大家理解我的数据结构和距离计算逻辑:
import java.util.Random; public class Search { static class Point { static final int MAXIMUM = 100; final int x; final int y; final int z; Point(int x, int y, int z) { this.x = verify(x); this.y = verify(y); this.z = verify(z); } Point(Random random) { this(random.nextInt(Point.MAXIMUM), random.nextInt(Point.MAXIMUM), random.nextInt(Point.MAXIMUM)); } int getDistance(Point point) { return Math.abs(x - point.x) + Math.abs(y - point.y) + Math.abs(z - point.z); } static int verify(int value) { if ((0 > value) || (MAXIMUM < value)) { throw new IllegalArgumentException(); } return value; } } static class Line { final Point[] points; Line(Point[] points) { this.points = points; } Line(Random random, int indices) { points = new Point[indices]; for (int j = 0; j < points.length; j++) { points[j] = new Point(random); } } int getDistance(Line line) { if (points.length != line.points.length) { throw new IllegalArgumentException(); } int distance = 0; for (int i = 0; i < points.length; i++) { distance += points[i].getDistance(line.points[i]); } return distance; } } static class DataSet { final int indices; final Line[] lines; DataSet(Line[] lines) { // TODO - Verify all lines have the same number of points indices = lines[0].points.length; this.lines = lines; } DataSet(Random random) { indices = 1 + random.nextInt(99); lines = new Line[1 + random.nextInt(999999)]; for (int i = 0; i < lines.length; i++) { lines[i] = new Line(random, indices); } } Line findClosest(Line line) { int closestDistance = Integer.MAX_VALUE; Line closestLine = null; for (Line nextLine : lines) { int distance = line.getDistance(nextLine); if ((Integer.MAX_VALUE == closestDistance) || (distance < closestDistance)) { closestDistance = distance; closestLine = nextLine; } } return closestLine; } } public static void main(String[] arguments) { final Random random = new Random(); final DataSet dataSet = new DataSet(random); final Line arbitraryLine1 = new Line(random, dataSet.indices); final Line arbitraryLine2 = new Line(random, dataSet.indices); final Line arbitraryLineM = new Line(random, dataSet.indices); dataSet.findClosest(arbitraryLine1); dataSet.findClosest(arbitraryLine2); dataSet.findClosest(arbitraryLineM); } }
我的具体需求与疑问
- 现有线性搜索在百万级数据集下性能太差,有没有更高效的查找算法能适配这种多段线的场景?
- 我知道k-d树适合单点近邻查找,但怎么扩展到这种由多个点组成的多段线?或者有没有其他更适合的空间索引结构?
- 我的坐标值可以做任意归一化处理(比如缩放到[0,1000]、[-100,100]或者[-1.0,1.0]区间),如果归一化能帮助优化算法,完全可以做。
- 除了找最近的一条线,后续还需要支持返回前Q个最近的线,这个需求也要考虑进去。
- 数据集的线可以预先排序或者构建索引结构——只要能加速后续的查找,预处理的时间我完全可以接受。
有没有大佬能给我指条明路?最好能有Java相关的实现思路或者参考方案,万分感谢!
内容来源于stack exchange




