在一个方框中找到2d点

时间:2013-02-13 13:26:52

标签: java algorithm 2d point

我正在寻找一种算法来获得最快的方法来找到一个盒子里的所有点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());
}
}

3 个答案:

答案 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)

您的解决方案是线性的,您无法做得更好,因为您至少要阅读输入数据。