最近的点对算法
Posted
技术标签:
【中文标题】最近的点对算法【英文标题】:Closest pair of points algorithm 【发布时间】:2012-03-13 10:07:54 【问题描述】:我正在尝试实现该算法的更简单版本,但它比二次算法效果更好。我的想法基本上是仅通过 x 坐标对点进行排序并尝试从那里解决它。一旦我按 x 坐标对点数组进行排序,我想遍历数组并基本上跳过距离大于我所取的前两个点的点。
比如我的currentminDist = x;
如果我正在查看的两对点的距离 > x(仅通过其 x 坐标距离),我将忽略该点并在数组中移过它。
我有这个想法,但我有点坚持如何实际实现这一点(尤其是条件部分)。我有一个函数可以根据它们的 x 坐标返回两点之间的距离。
我对如何为循环实际编写条件感到困惑,因为如果距离恰好太远,我想忽略一个点,并且仍然填写我的数组,该数组将包含每个 i 的最近点的答案(我是我正在关注的当前点)。
任何提示或指示将不胜感激。我对编码算法不是很了解,所以这很令人沮丧。
这是我的部分代码:
for (i = 0; i < numofmypoints; i++)
for (int j = i + 1; (j < numpofmypoints) && ((inputpoints[j].x - inputpoints[i].x) < currbest); j++ )
currdist = Auxilary.distbyX(inputpoints[i],inputpoints[j]);
if (currdist < bestdist)
closest[i] = j;
bestdist = currdist;
distbyX 是我的函数,它只返回两点之间的距离。
谢谢!
【问题讨论】:
@Paul:你需要经常这样做吗?将您的积分存储在“四叉树”中没有帮助吗? en.wikipedia.org/wiki/Quadtree 请注意,您可能会获得比使用朴素算法更好的性能,但您仍然会是O(n^2)
。
为什么你的代码中有currbest
和bestdist
?有什么区别?
【参考方案1】:
使用 KD-Tree 的快速算法 该算法创建一个 kd-tree,然后为每个点找到最接近的对。创建kd树是O(n log2n),找到一个点的最近邻是O(logn)。必须归功于Wikipedia,在一篇文章中解释了如何创建 kd-trees 以及如何使用它们来找到最近的邻居。
import java.util.*;
public class Program
public static void main(String[] args)
List<Point> points = generatePoints();
Point[] closest = new Point[points.size()];
KDTree tree = new KDTree(points, 0); // WILL MODIFY 'points'
for (int i = 0; i < points.size(); i++)
closest[i] = tree.findClosest(points.get(i));
for (int i = 0; i < points.size(); i++)
System.out.println(points.get(i) + " is closest to " + closest[i]);
private static List<Point> generatePoints()
ArrayList<Point> points = new ArrayList<Point>();
Random r = new Random();
for (int i = 0; i < 1000; i++)
points.add(new Point(r.nextInt() % 1000, r.nextInt() % 1000));
return points;
class Point
public static final Point INFINITY
= new Point(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY);
public double[] coord; // coord[0] = x, coord[1] = y
public Point(double x, double y)
coord = new double[] x, y ;
public double getX() return coord[0];
public double getY() return coord[1];
public double distance(Point p)
double dX = getX() - p.getX();
double dY = getY() - p.getY();
return Math.sqrt(dX * dX + dY * dY);
public boolean equals(Point p)
return (getX() == p.getX()) && (getY() == p.getY());
public String toString()
return "(" + getX() + ", " + getY() + ")";
public static class PointComp implements Comparator<Point>
int d; // the dimension to compare in (0 => x, 1 => y)
public PointComp(int dimension)
d = dimension;
public int compare(Point a, Point b)
return (int) (a.coord[d] - b.coord[d]);
class KDTree
// 2D k-d tree
private KDTree childA, childB;
private Point point; // defines the boundary
private int d; // dimension: 0 => left/right split, 1 => up/down split
public KDTree(List<Point> points, int depth)
childA = null;
childB = null;
d = depth % 2;
// find median by sorting in dimension 'd' (either x or y)
Comparator<Point> comp = new Point.PointComp(d);
Collections.sort(points, comp);
int median = (points.size() - 1) / 2;
point = points.get(median);
// Create childA and childB recursively.
// WARNING: subList() does not create a true copy,
// so the original will get modified.
if (median > 0)
childA = new KDTree(
points.subList(0, median),
depth + 1);
if (median + 1 < points.size())
childB = new KDTree(
points.subList(median + 1, points.size()),
depth + 1);
public Point findClosest(Point target)
Point closest = point.equals(target) ? Point.INFINITY : point;
double bestDist = closest.distance(target);
double spacing = target.coord[d] - point.coord[d];
KDTree rightSide = (spacing < 0) ? childA : childB;
KDTree otherSide = (spacing < 0) ? childB : childA;
/*
* The 'rightSide' is the side on which 'target' lies
* and the 'otherSide' is the other one. It is possible
* that 'otherSide' will not have to be searched.
*/
if (rightSide != null)
Point candidate = rightSide.findClosest(target);
if (candidate.distance(target) < bestDist)
closest = candidate;
bestDist = closest.distance(target);
if (otherSide != null && (Math.abs(spacing) < bestDist))
Point candidate = otherSide.findClosest(target);
if (candidate.distance(target) < bestDist)
closest = candidate;
bestDist = closest.distance(target);
return closest;
修复问题中的代码
如果您真的不担心复杂性,那么您的代码的唯一问题是您向前看而不是向后看。只需复制内部循环并使j
从(i - 1)
变为0
:
Point[] points = sort(input());
int[] closest = new int[points.length];
for (int i = 0; i < points.length; i++)
double bestdist = Double.POSITIVE_INFINITY;
for (int j = i + 1; (j < points.length) && ((points[j].x - points[i].x) < bestdist); j++ )
double currdist = dist(points[i], points[j]);
if (currdist < bestdist)
closest[i] = j;
bestdist = currdist;
for (int j = i - 1; (j >= 0) && ((points[i].x - points[j].x) < bestdist); j-- )
double currdist = dist(points[i], points[j]);
if (currdist < bestdist)
closest[i] = j;
bestdist = currdist;
【讨论】:
我不担心最坏的情况。我假设所有 x 值都是不同的。这就是为什么我想尝试按照我提出的方式解决它。你的方法很有意义,我可以使用数据结构来解决它,但我想知道它是否可以按照我描述的方式解决。我遇到了它没有计算所有点的最近点的问题,它只计算其中一些点,其余的都是一遍又一遍地重复的同一个点。所以这就是为什么我想看看我是否在某个地方出错了。 经典的“最近点对”问题是找到彼此最接近的点对。直到现在我才意识到你的问题是一个不同的问题 - 为每个点找到最近的邻居。想到算法后,我会尽快更新答案。 @Paul:我想不出办法将你的扫描线提高到 O(good),所以我使用了 kd-tree。 啊,是的,我查看了 kd 树的实现。我在使用数据结构时使用了四叉树来解决它。但是我想实现一个更简单的扫描线算法,但我不知道为什么我的数组没有被填写。现在看来,这很有意义。以上是关于最近的点对算法的主要内容,如果未能解决你的问题,请参考以下文章