【发布时间】:2022-01-25 05:30:21
【问题描述】:
我正在尝试开发分治算法来查找点数组中最近的 2 个点。
我发现当输入有几个点具有相同的 x 坐标时。然后,如果其中 2 个在 Section1 和 Section2 之间划分,我会得到不正确的结果。我在这里显示一个最小的输入只是为了显示问题。 (以及更大的输入以表明问题也存在于更大的输入中)。我担心的是当我有许多具有相同 x 坐标的点时。
我想到了一个解决方案。如果我扩展 Point2D 类并添加一个字段来存储 pointsSortedOnX 数组中的点索引,那么我可以从这个类创建 pointsSortedOnY 数组。现在,当我通过pointsSortedOnY 数组而不是检查该点是否等于或小于midX 时,我可以检查它是否小于或等于mid 的pointsSortedOnX 索引。
我只是想知道是否有更好的方法。
输入(最小):
double[][] p = {
{25.6, 67.9},
{25.6, 8.65},
{32.35, 81.26},
{25.32, 67.15},
}
输入(显示问题也在于较大的输入):
double[][] p = {
{25.6, 67.9},
{25.6, 8.65},
{32.35, 81.26},
{25.32, 67.15},
{25.6, 60.95},
{25.6, 56.79},
{25.6, 78.49},
{25.6, 6.23},
{25.6, 7.9},
{25.6, 95.9},
{25.6, 35.9},
{25.6, 10.9},
}
代码:
/** Return the distance of the closest PointPair of points */
public static PointPair getClosestPair(double[][] points) {
int size = points.length;
pointsSortedX = new Point2D.Double[size];
pointsSortedY = new Point2D.Double[size];
for (int i = 0; i < size; i++) {
pointsSortedX[i] = new Point2D.Double(points[i][0], points[i][1]);
pointsSortedY[i] = new Point2D.Double(points[i][0], points[i][1]);
}
Arrays.sort(pointsSortedX, (e1, e2) ->
(e1.getX() == e2.getX() ? (e1.getY() > e2.getY() ? 1 : (e1.getY() < e2.getY() ? -1 : 0)) : (e1.getX() > e2.getX() ? 1 : -1)));
Arrays.sort(pointsSortedY, (e1, e2) ->
(e1.getY() > e2.getY() ? 1 : (e1.getY() < e2.getY() ? -1 : 0)));
return findNearstPair(pointsSortedX, 0, size - 1, pointsSortedY, 1);
}
/** Return the distance of the closest PointPair of points
* in pointsOrderedOnX[low..high]. This is a recursive
* method. pointsOrderedOnX and pointsOrderedOnY are
* not changed in the subsequent recursive calls.
*/
public static PointPair findNearstPair(Point2D[] pointsOrderedOnX, int low, int high, Point2D[] pointsOrderedOnY) {
if (low == high)
return new PointPair(pointsOrderedOnX[low], pointsOrderedOnX[high], Double.MAX_VALUE);
else if ((high - low) == 1)
return new PointPair(pointsOrderedOnX[low], pointsOrderedOnX[high], pointsOrderedOnX[low].distance(pointsOrderedOnX[high]));
else{
// Divide
int mid = (high - low) / 2;
PointPair leftPair = findNearstPair(pointsOrderedOnX, low, low + mid, pointsOrderedOnY);
PointPair rightPair = findNearstPair(pointsOrderedOnX, low + mid + 1, high, pointsOrderedOnY);
// Merge the right & left
shortestPair = leftPair.distance > rightPair.distance ? rightPair : leftPair;
System.out.println("shortest " + shortestPair);
// Find points in pointsOrderedOnY that belong in stripL & stripR
// this any x that is less then d away from the middle of
// the 2 sections (between mid & mid + 1)
double minXinS1 = pointsOrderedOnX[low].getX();
// <= midX is S1(left side) > midX is S2(right side)
double midX = pointsOrderedOnX[low + mid].getX();
double maxXinS2 = pointsOrderedOnX[high].getX();
List<Integer> stripL = new ArrayList<Integer>();
List<Integer> stripR = new ArrayList<Integer>();
for(int p = 0; p < pointsOrderedOnY.length; p++){
double pX = pointsOrderedOnY[p].getX();
double pY =pointsOrderedOnY[p].getY();
// is in S1 & less then distance away from middle
// >= minXinS1 & <= midX & >= midX - d = stripL
if(pX <= midX && pX >= minXinS1 && pX >= midX - shortestPair.distance){
stripL.add(p);
}
//is in S2 & less then distance away from middle
// > midX && <= maxXinS2 & > midX & < midX + d
else if(pX > midX && pX <= maxXinS2 && pX < midX + shortestPair.distance){
stripR.add(p);
}
}
// For each point in stripL find the points in stripR that are less then
// distance away (max 6 points in stripR)
int stripRIndx = 0; //
for(int p = 0; p < stripL.size(); p++){
double pY = pointsOrderedOnY[stripL.get(p)].getY();
// Skip points in stripR that are below pY - distance
while(stripRIndx < stripR.size() && pointsOrderedOnY[stripR.get(stripRIndx)].getY()
<= pY - shortestPair.distance)
stripRIndx++;
// Check points in stripR that ate within pY +/- distance
int tempStripRIndex = stripRIndx;
while(tempStripRIndex < stripR.size() && pointsOrderedOnY[stripR.get(tempStripRIndex)].getY()
<= pY + shortestPair.distance){
if(pointsOrderedOnY[stripL.get(p)].distance(pointsOrderedOnY[stripR.get(tempStripRIndex)]) < shortestPair.distance){
shortestPair.p1 = pointsOrderedOnY[stripL.get(p)];
shortestPair.p2 = pointsOrderedOnY[stripR.get(tempStripRIndex)];
shortestPair.distance = pointsOrderedOnY[stripL.get(p)].distance(pointsOrderedOnY[stripR.get(tempStripRIndex)]);
}
tempStripRIndex++;
}
}
return shortestPair;
}
}
class PointPair {
Point2D p1;
Point2D p2;
double distance = Double.MAX_VALUE;
PointPair(){}
PointPair(Point2D p1, Point2D p2, double distance) {
this.p1 = p1;
this.p2 = p2;
this.distance = distance;
}
@Override
public String toString(){
return p1 + ":" + p2 + " distance = " + distance;
}
编辑 我见过this question。我正在寻找我的代码中的错误以及如何解决它。我正在尝试实现与那里提到的相同的想法。
【问题讨论】:
-
这能回答你的问题吗? Find two closest points in 2D distribution
-
@Progman 第一个答案是我不熟悉的 C++。第二个答案是指*。我现在谈到我的问题中提到的分而治之。我的问题在于他的实施。如果您有机会,请告诉我我在答案中实施它的方式是否正确。它似乎并不比蛮力实施更快。
标签: java nearest-neighbor divide-and-conquer