如何使两点算法之间的最短路径更快?
How to make shortest path between two points algorithm faster?
我写了这个算法。它有效(至少对于我的简短测试用例),但在较大的输入上花费的时间太长。我怎样才能让它更快?
// Returns an array of length 2 with the two closest points to each other from the
// original array of points "arr"
private static Point2D[] getClosestPair(Point2D[] arr)
{
int n = arr.length;
float min = 1.0f;
float dist = 0.0f;
Point2D[] ret = new Point2D[2];
// If array only has 2 points, return array
if (n == 2) return arr;
// Algorithm says to brute force at 3 or lower array items
if (n <= 3)
{
for (int i = 0; i < arr.length; i++)
{
for (int j = 0; j < arr.length; j++)
{
// If points are identical but the point is not looking
// at itself, return because shortest distance is 0 then
if (i != j && arr[i].equals(arr[j]))
{
ret[0] = arr[i];
ret[1] = arr[j];
return ret;
}
// If points are not the same and current min is larger than
// current stored distance
else if (i != j && dist < min)
{
dist = distanceSq(arr[i], arr[j]);
ret[0] = arr[i];
ret[1] = arr[j];
min = dist;
}
}
}
return ret;
}
int halfN = n/2;
// Left hand side
Point2D[] LHS = Arrays.copyOfRange(arr, 0, halfN);
// Right hand side
Point2D[] RHS = Arrays.copyOfRange(arr, halfN, n);
// Result of left recursion
Point2D[] LRes = getClosestPair(LHS);
// Result of right recursion
Point2D[] RRes = getClosestPair(RHS);
float LDist = distanceSq(LRes[0], LRes[1]);
float RDist = distanceSq(RRes[0], RRes[1]);
// Calculate minimum of both recursive results
if (LDist > RDist)
{
min = RDist;
ret[0] = RRes[0];
ret[1] = RRes[1];
}
else
{
min = LDist;
ret[0] = LRes[0];
ret[1] = LRes[1];
}
for (Point2D q : LHS)
{
// If q is close to the median line
if ((halfN - q.getX()) < min)
{
for (Point2D p : RHS)
{
// If p is close to q
if ((p.getX() - q.getX()) < min)
{
dist = distanceSq(q, p);
if (!q.equals(p) && dist < min)
{
min = dist;
ret[0] = q;
ret[1] = p;
}
}
}
}
}
return ret;
}
private static float distanceSq(Point2D p1, Point2D p2)
{
return (float)Math.pow((p1.getX() - p2.getX()) + (p1.getY() - p2.getY()), 2);
}
我大致遵循此处解释的算法:http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairDQ.html
以及此处带有伪代码的不同资源:
http://i.imgur.com/XYDTfBl.png
我无法更改函数的 return 类型,也无法添加任何新参数。
感谢您的帮助!
您可以做几件事。
首先,您可以非常简单地将程序花费的时间缩短到 运行,方法是仅在 "reminder" 点上将第二次迭代更改为 运行。这有助于您避免为每个值同时计算 (i,j)
和 (j,i)
。为此,只需更改:
for (int j = 0; j < arr.length; j++)
到
for (int j = i+1; j < arr.length; j++)
不过这仍然是 O(n^2)
。
您可以通过迭代点并将每个点存储在智能数据结构中来实现 O(nlogn)
时间(最有可能是 kd-tree)。在每次插入之前,找到已经存储在DS中的最近点(kd-tree在O(logn)
时间内支持这个),它是你的最小距离候选。
我相信链接算法提到了按一个坐标对数组进行排序,以便给定点 1 到 2000 中的 LHS q,如果点 200 处的 RHS p 超过 'min' 距离,只有它的 x 距离,您可以避免检查剩余的 201 到 2000 点。
我想通了 - 大大缩短了时间。 distanceSq
函数错误。最好改用 Java 的 Point2D somepoint.distanceSq(otherpoint);
方法。
至于当n
为3时的原始暴力(在那种情况下它只会是3或2),线性搜索更好更有效。
在暴力条件之后的内部 for
循环中,针对 min
变量的检查也是错误的。使用平方距离很好,但 min
不是平方的。它保留了原始距离,这意味着 min
必须在两次检查中都平方根(一次在外循环中,一次在每次检查的内部循环中)。
所以,
if ((p.getX() - q.getX()) < min)
应该是
if ((p.getX() - q.getX()) < Math.sqrt(min))
另一张支票也一样。
谢谢大家的回答!
我写了这个算法。它有效(至少对于我的简短测试用例),但在较大的输入上花费的时间太长。我怎样才能让它更快?
// Returns an array of length 2 with the two closest points to each other from the
// original array of points "arr"
private static Point2D[] getClosestPair(Point2D[] arr)
{
int n = arr.length;
float min = 1.0f;
float dist = 0.0f;
Point2D[] ret = new Point2D[2];
// If array only has 2 points, return array
if (n == 2) return arr;
// Algorithm says to brute force at 3 or lower array items
if (n <= 3)
{
for (int i = 0; i < arr.length; i++)
{
for (int j = 0; j < arr.length; j++)
{
// If points are identical but the point is not looking
// at itself, return because shortest distance is 0 then
if (i != j && arr[i].equals(arr[j]))
{
ret[0] = arr[i];
ret[1] = arr[j];
return ret;
}
// If points are not the same and current min is larger than
// current stored distance
else if (i != j && dist < min)
{
dist = distanceSq(arr[i], arr[j]);
ret[0] = arr[i];
ret[1] = arr[j];
min = dist;
}
}
}
return ret;
}
int halfN = n/2;
// Left hand side
Point2D[] LHS = Arrays.copyOfRange(arr, 0, halfN);
// Right hand side
Point2D[] RHS = Arrays.copyOfRange(arr, halfN, n);
// Result of left recursion
Point2D[] LRes = getClosestPair(LHS);
// Result of right recursion
Point2D[] RRes = getClosestPair(RHS);
float LDist = distanceSq(LRes[0], LRes[1]);
float RDist = distanceSq(RRes[0], RRes[1]);
// Calculate minimum of both recursive results
if (LDist > RDist)
{
min = RDist;
ret[0] = RRes[0];
ret[1] = RRes[1];
}
else
{
min = LDist;
ret[0] = LRes[0];
ret[1] = LRes[1];
}
for (Point2D q : LHS)
{
// If q is close to the median line
if ((halfN - q.getX()) < min)
{
for (Point2D p : RHS)
{
// If p is close to q
if ((p.getX() - q.getX()) < min)
{
dist = distanceSq(q, p);
if (!q.equals(p) && dist < min)
{
min = dist;
ret[0] = q;
ret[1] = p;
}
}
}
}
}
return ret;
}
private static float distanceSq(Point2D p1, Point2D p2)
{
return (float)Math.pow((p1.getX() - p2.getX()) + (p1.getY() - p2.getY()), 2);
}
我大致遵循此处解释的算法:http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairDQ.html
以及此处带有伪代码的不同资源:
http://i.imgur.com/XYDTfBl.png
我无法更改函数的 return 类型,也无法添加任何新参数。
感谢您的帮助!
您可以做几件事。
首先,您可以非常简单地将程序花费的时间缩短到 运行,方法是仅在 "reminder" 点上将第二次迭代更改为 运行。这有助于您避免为每个值同时计算 (i,j)
和 (j,i)
。为此,只需更改:
for (int j = 0; j < arr.length; j++)
到
for (int j = i+1; j < arr.length; j++)
不过这仍然是 O(n^2)
。
您可以通过迭代点并将每个点存储在智能数据结构中来实现 O(nlogn)
时间(最有可能是 kd-tree)。在每次插入之前,找到已经存储在DS中的最近点(kd-tree在O(logn)
时间内支持这个),它是你的最小距离候选。
我相信链接算法提到了按一个坐标对数组进行排序,以便给定点 1 到 2000 中的 LHS q,如果点 200 处的 RHS p 超过 'min' 距离,只有它的 x 距离,您可以避免检查剩余的 201 到 2000 点。
我想通了 - 大大缩短了时间。 distanceSq
函数错误。最好改用 Java 的 Point2D somepoint.distanceSq(otherpoint);
方法。
至于当n
为3时的原始暴力(在那种情况下它只会是3或2),线性搜索更好更有效。
在暴力条件之后的内部 for
循环中,针对 min
变量的检查也是错误的。使用平方距离很好,但 min
不是平方的。它保留了原始距离,这意味着 min
必须在两次检查中都平方根(一次在外循环中,一次在每次检查的内部循环中)。
所以,
if ((p.getX() - q.getX()) < min)
应该是
if ((p.getX() - q.getX()) < Math.sqrt(min))
另一张支票也一样。
谢谢大家的回答!