我正在寻找一种算法来获得最快的方法来找到一个盒子里的所有点2D(x,y)(一个盒子由2个点定义:lowerLeft和upperRight)。
想象一下,我们在2D空间中有200万个点。
在那个2D空间中,我在2点左右创建了一个盒子,一个是左下角,另一个是右上角。 获得包装盒中所有积分的最快方法是什么? 这是最糟糕场景的java测试:循环每个点(2百万!)并确定它是否在框内。 如果首先订购点列表,我相信我们可以变得更快......
你有想法吗?
public class FindPointsInBox {
public static void main(String[] args) throws Exception {
// List of 2,000,000 points (x,y)
List<Point> allPoints = new ArrayList<Point>();
for(int i=0; i<2000000; i++) {
allPoints.add(new Point(46 - (Math.random()), -74 - (Math.random())));
}
// Box defined by 2 points: lowerLeft and upperRight
List<Point> pointsInBox = new ArrayList<Point>();
Point lowerLeft = new Point(44.91293325430085, -74.25107363281245);
Point upperRight = new Point(45.3289676752705, -72.93820742187495);
Date t1 = new Date();
// TODO: What is the fastest way to find all points contained in box
for(int i=0; i<allPoints.size(); i++) {
if(isPointInBox(allPoints.get(i), lowerLeft, upperRight))
pointsInBox.add(allPoints.get(i));
}
Date t2 = new Date();
System.out.println(pointsInBox.size() + " points in box");
System.out.println(t2.getTime()-t1.getTime() + "ms");
}
private static boolean isPointInBox(Point p, Point lowerLeft, Point upperRight) {
return (
p.getX() >= lowerLeft.getX() &&
p.getX() <= upperRight.getX() &&
p.getY() >= lowerLeft.getY() &&
p.getY() <= upperRight.getY());
}
}
答案 0 :(得分:4)
改进米哈伊尔斯回答(我还不能发表评论)你可以使用四叉树http://en.wikipedia.org/wiki/Quadtree。我认为这就是米哈伊尔所说的,并且通过将空间划分为网格来实现。如果分区中有许多点,则它本身被划分为一个小网格。
选择点时,可以比较分区的范围,如果它们的包含矩形与选择矩形不相交,则快速排除几个点。
四叉树平均需要O(n log n)运算才能创建,而选择一堆点需要O(log n)。
答案 1 :(得分:3)
将您的空间拆分为方形单元格。对于位于单元格中的每个单元存储点列表。对于给定的矩形,首先找到与其相交的所有单元格,然后迭代这些单元格中的点并测试它们中的哪些在矩形中。以下代码演示了这种方法:
public class PointsIndex {
private final int width;
private final int height;
private final int rows;
private final int cols;
private final List<Point> [][] cells;
@SuppressWarnings("unchecked")
public PointsIndex (
int width, int height, int rows, int cols)
{
this.width = width;
this.height = height;
this.rows = rows;
this.cols = cols;
cells = (List<Point> [][])new List<?> [rows][];
for (int i = 0; i < rows; i++)
cells [i] = (List<Point> [])new List<?> [cols];
}
public void addPoint (int x, int y)
{
int r = x * rows / width;
int c = y * cols / height;
List <Point> cell = cells [r][c];
if (cell == null)
{
cell = new ArrayList<Point>();
cells [r][c] = cell;
}
cell.add (new Point (x, y));
}
public Point [] getPoints (int x, int y, int w, int h)
{
int r1 = x * rows / width;
int r2 = (x + w - 1) * rows / width;
int c1 = y * cols / height;
int c2 = (y + h - 1) * cols / height;
ArrayList<Point> result = new ArrayList<Point>();
for (int r = r1; r <= r2; r++)
for (int c = c1; c <= c2; c++)
{
List <Point> cell = cells [r][c];
if (cell != null)
{
if (r == r1 || r == r2 || c == c1 || c == c2)
{
for (Point p: cell)
if (p.x > x && p.x < x + w && p.y > y && p.y < y + h)
result.add (p);
}
else result.addAll (cell);
}
}
return result.toArray(new Point [result.size()]);
}
public static void main(String[] args) {
Random r = new Random ();
PointsIndex index = new PointsIndex(1000000, 1000000, 100, 100);
List <Point> points = new ArrayList<Point>(1000000);
for (int i = 0; i < 1000000; i++)
{
int x = r.nextInt(1000000);
int y = r.nextInt(1000000);
index.addPoint(x, y);
points.add (new Point (x, y));
}
long t;
t = System.currentTimeMillis();
Point [] choosen1 = index.getPoints(456789, 345678, 12345, 23456);
System.out.println (
"Fast method found " + choosen1.length + " points in " +
(System.currentTimeMillis() - t) + " ms");
Rectangle rect = new Rectangle (456789, 345678, 12345, 23456);
List <Point> choosen2 = new ArrayList<Point>();
t = System.currentTimeMillis();
for (Point p: points)
{
if (rect.contains(p))
choosen2.add (p);
}
System.out.println(
"Slow method found " + choosen2.size () + " points in " +
(System.currentTimeMillis() - t) + " ms");
}
}
答案 2 :(得分:2)
您的解决方案是线性的,您无法做得更好,因为您至少要阅读输入数据。