【问题标题】:Get all nearest points in array获取数组中所有最近的点
【发布时间】:2017-11-01 11:04:21
【问题描述】:

我有一个二维数组,我在其中存储一些点以获得最近的两个点,例如: "(-1, 3), (-1, -1), (1, 1), (2, 0.5) , (2, -1) , (3, 3) ,(4, 2) , (4, 0.5)" 结果是:"(1.0, 1.0) and (2.0, 0.5)" 而且效果很好:

   Scanner scanner = new Scanner(System.in);
    System.out.println("Enter the number of points");
    int numberOfPoints = scanner.nextInt();

    //setting number of rows, number of column is unable to change
    double[][] points = new double[numberOfPoints][2];
    for (int i = 0; i < points.length; i++) {
        points[i][0] = scanner.nextDouble();
        points[i][1] = scanner.nextDouble();
    }

    int point1 = 0, point2 = 1;
    double shortestDistance = distance(points[point1][0], points[point1][1],
            points[point2][0], points[point2][1]);

    //get shortest distance
    for (int i = 0; i < points.length; i++) {
        for (int j = i + 1; j < points.length; j++) {
            double distance = distance(points[i][0], points[i][1],
                    points[j][0], points[j][1]);

            if (shortestDistance > distance) {

                point1 = i;
                point2 = j;
                shortestDistance = distance;
            }

        }
    }

    System.out.println("The closest two points is" +
            "(" + points[point1][0] + ", " + points[point1][1] + ") and (" +
            points[point2][0] + ", " + points[point2][1] + ")");

}

public static double distance(double x1, double y1, double x2, double y2) {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}

我试图获得所有最近的点,而不仅仅是两个点。

我试过通过这种方式获取,但它并没有涵盖所有情况,也没有显示所有点:

for (int i = 0; i < points.length; i++) {
        for (int j = 0; j < points.length; j++) {
            if (distance(points[i][0], points[i][1],
                    points[j][0], points[j][1]) == shortestDistance)
                System.out.println("The closest two points are " +
                        "(" + points[i][0] + ", " + points[i][1] + ") and (" +
                        points[j][0] + ", " + points[j][1] + ")");
        }
    }

我也尝试初始化新数组并将距离存储到其中然后对其进行排序,但它失败了。

如何显示所有最近的点?

注意:

我没有发现 this question 对我有用。

【问题讨论】:

  • “但它并没有涵盖所有情况” 它在什么方面没有涵盖所有情况?请举个例子。
  • @Andreas 上面的输入只是输出两个点而不是四个。
  • 什么4分?没有其他点相距 1.118033988749895。
  • 为了证明这一点,here 是打印所有点对之间的所有距离列表的代码,按距离排序。不比较反向对,因为(1.0, 1.0)(2.0, 0.5) 之间的距离当然与(2.0, 0.5)(1.0, 1.0) 之间的距离相同。如您所见,只有一对点相距1.118033988749895
  • 所以您的意思是,当您找到彼此最接近的 2 个点 ((1.0, 1.0) - (2.0, 0.5)) 时,您想消除那些然后找到下一个 now 的“对” 彼此最接近((3.0, 3.0) - (4.0, 2.0)),然后重复((2.0, -1.0) - (4.0, 0.5)),最后得到最后一对((-1.0, 3.0) - (-1.0, -1.0))?如果是这样,那么您应该更新问题并实际,因为我无法从现在的问题中得到答案。

标签: java arrays algorithm math


【解决方案1】:

下面comment澄清了问题的实际目标:

所以您的意思是,当您找到彼此最接近的 2 个点 ((1.0, 1.0) - (2.0, 0.5)) 时,您想要消除那些然后找到下一个 现在 最接近的“对”彼此((3.0, 3.0) - (4.0, 2.0)),然后重复((2.0, -1.0) - (4.0, 0.5)),最后得到最后一对((-1.0, 3.0) - (-1.0, -1.0))?

为此,您应该首先创建一个Point 类,这样您就可以轻松跟踪已使用的点。如果你不想自己写,可以使用java.awt.geom.Point2D.Double

您还应该创建一个用于跟踪点对的类,以便您可以轻松地按距离对点进行排序。它可以命名任何东西,但我在下面将其命名为Distance

现在的逻辑是创建所有可能的Distance 对象的列表,即点对。然后按距离排序。

第一个列表元素现在是彼此最近的对。然后迭代列表以查找下一对最近的点,跳过任何使用已使用点的点。

如果不止一对同样“最近”,则下面的逻辑将只选择其中一个。一旦您定义了该场景中实际应该发生的情况,您就可以更改该行为。

public static void main(String[] args) {
    double[][] pointValues = { {-1, 3}, {-1, -1}, {1, 1}, {2, 0.5}, {2, -1}, {3, 3}, {4, 2}, {4, 0.5} };

    Point[] points = new Point[pointValues.length];
    for (int i = 0; i < points.length; i++)
        points[i] = new Point(pointValues[i][0], pointValues[i][1]);

    List<Distance> distances = new ArrayList<>();
    for (int i = 0; i < points.length; i++)
        for (int j = i + 1; j < points.length; j++)
            distances.add(new Distance(points[i], points[j]));
    Collections.sort(distances);

    Set<Point> used = new HashSet<>();
    for (Distance distance : distances) {
        if (! used.contains(distance.getPoint1()) && ! used.contains(distance.getPoint2())) {
            System.out.println(distance);
            used.add(distance.getPoint1());
            used.add(distance.getPoint2());
        }
    }
}
public final class Point {
    private final double x;
    private final double y;
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    public double getX() {
        return this.x;
    }
    public double getY() {
        return this.y;
    }
    @Override
    public String toString() {
        return "(" + this.x + ", " + this.y + ")";
    }
    @Override
    public int hashCode() {
        return Double.hashCode(this.x) * 31 + Double.hashCode(this.y);
    }
    @Override
    public boolean equals(Object obj) {
        if (! (obj instanceof Point))
            return false;
        Point that = (Point) obj;
        return (Double.doubleToLongBits(this.x) == Double.doubleToLongBits(that.x)
             && Double.doubleToLongBits(this.y) == Double.doubleToLongBits(that.y));
    }
}
public final class Distance implements Comparable<Distance> {
    private final Point  p1;
    private final Point  p2;
    private final double distance;
    public Distance(Point p1, Point p2) {
        this.p1 = p1;
        this.p2 = p2;
        this.distance = Math.hypot(p2.getX() - p1.getX(), p2.getY() - p1.getY());
    }
    public Point getPoint1() {
        return this.p1;
    }
    public Point getPoint2() {
        return this.p2;
    }
    public double getDistance() {
        return this.distance;
    }
    @Override
    public String toString() {
        return String.format("%-12s - %-12s: %s", this.p1, this.p2, this.distance);
    }
    @Override
    public int compareTo(Distance that) {
        return Double.compare(this.distance, that.distance);
    }
}

输出

(1.0, 1.0)   - (2.0, 0.5)  : 1.118033988749895
(3.0, 3.0)   - (4.0, 2.0)  : 1.4142135623730951
(2.0, -1.0)  - (4.0, 0.5)  : 2.5
(-1.0, 3.0)  - (-1.0, -1.0): 4.0

【讨论】:

    【解决方案2】:

    首先是一个优化技巧:使用平方而不是距离(以降为根)。

    public static double distance2(double x1, double y1, double x2, double y2) {
        return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    }
    

    取所有最近的点需要维护一个 (i, j) 的列表。

    List<int[]> nearestPointIndices = new ArrayList<>();
    double shortestDistance2 = Double.MAX_VALUE;
    
    for (int i = 0; i < points.length; i++) {
        for (int j = i + 1; j < points.length; j++) {
            double distance2 = distance2(points[i][0], points[i][1],
                    points[j][0], points[j][1]);
    
            if (shortestDistance2 >= distance2) {
                if (shortestDistance2 > distance2) {
                    nearestPointIndices.clear();
                    shortestDistance2 = distance2;
                }
                nearestPointIndices.add(new int[] { i, j });
            }
        }
    }
    

    这是一个收集到 (i, j) 的最近点的列表,并且 要么在更近的点上清除该列表,要么在相同的近点上添加到列表中。


    另外值得一提的是Sqrt等Math类的另一个功能:

    public static double distance(double x1, double y1, double x2, double y2) {
        return Math.hypot((x2 - x1), (y2 - y1)); // Hypotenuse sqrt(a² + b²).
    }
    

    【讨论】:

    • 这以何种方式解决了问题的重点,即 OP 错误地认为存在不止一对具有相同“最短”距离的点? 参考: "我试过用这种方式得到它,但是它不涵盖所有情况并且不显示所有点 ”,在comment 中跟进:“上面的输入只是输出两个点而不是四个”。只有一个最近的点。
    • @Andreas 可能有两对具有相同最小距离的点,例如 (2, 0)-(2, 1) 和 (4, 3)-(5, 3) 的 1。我没有手动查看 OPs 数据,也没有在数据上运行代码。 OPs 代码确实只给出了两点。我的不会。
    • @Andreas 我刚刚看到所涉及的评论轨道。我能说什么?我仍然希望我的回答对某些事情有好处,即使只是Math.hypot 来计算距离。
    • 只有一对,但你是对的,距离最短的可能不止一对,问题中的代码会打印所有这些(包括翻转对,加倍输出) .您的代码已优化,但在一次迭代中完成全部工作,而问题代码使用一次迭代来找到最短距离,并使用第二次迭代来打印具有该距离的所有对。优化很好,但不会改变结果。
    最近更新 更多