简体   繁体   English

如何使两点算法之间的最短路径更快?

[英]How to make shortest path between two points algorithm faster?

I wrote this algorithm. 我写了这个算法。 It works (at least with my short test cases), but takes too long on larger inputs. 它起作用(至少在我的短测试案例中),但在较大的输入上花费的时间太长。 How can I make it 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);
}

I am loosely following the algorithm explained here: http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairDQ.html 我松散地遵循这里解释的算法: http//www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairDQ.html

and a different resource with pseudocode here: 和伪代码的不同资源:

http://i.imgur.com/XYDTfBl.png http://i.imgur.com/XYDTfBl.png

I cannot change the return type of the function, or add any new arguments. 我无法更改函数的返回类型,或添加任何新参数。

Thanks for any help! 谢谢你的帮助!

There are several things you can do. 你可以做几件事。

First, you can very simply cut the time the program takes to run by changing the second iteration to run only on the "reminder" points. 首先,您可以通过将第二次迭代更改为仅在“提醒”点上运行来非常简单地缩短程序运行所需的时间。 This helps you to avoid calculating both (i,j) and (j,i) for each values. 这有助于您避免为每个值计算(i,j)(j,i) To do so, simply change: 为此,只需更改:

for (int j = 0; j < arr.length; j++)

to

for (int j = i+1; j < arr.length; j++)

This will still be O(n^2) though. 但这仍然是O(n^2)

You can achieve O(nlogn) time by iterating the points, and storing each in a smart data structure ( kd-tree most likely). 您可以通过迭代点并将每个点存储在智能数据结构(最有可能是kd树 )中来实现O(nlogn)时间。 Before each insertion, find the closest point already stored in the DS (the kd-tree supports this in O(logn) time), and it is your candidate for minimal distance. 在每次插入之前,找到已经存储在DS中的最近点(kd树在O(logn)时间内支持此值),并且它是最小距离的候选者。

我相信链接算法提到按一个坐标对数组进行排序,以便在第1点到第2000点给定LHS q,如果点200处的RHS p距离只有x距离超过“min”距离,则可以避免检查剩余的201到2000分。

I figured it out - cut the time by a vast amount. 我想通了 - 大量减少了时间。 The distanceSq function is wrong. distanceSq函数错误。 Best to use Java's Point2D somepoint.distanceSq(otherpoint); 最好使用Java的Point2D somepoint.distanceSq(otherpoint); method instead. 方法而不是。

As for the original brute force when n is 3 (it will only ever be 3 or 2 in that scenario), a linear search is better and more effective. 至于n为3时的原始蛮力(在那种情况下它只会是3或2),线性搜索更好,更有效。

The checks against the min variable are also wrong in the inner for loops after the brute force condition. 在蛮力条件之后,针对min变量的检查在内部for循环中也是错误的。 Using squared distance is fine, but min is not squared. 使用平方距离很好,但是min不是平方的。 It has preserved, original distance, which means that min must be square rooted in both checks (once in the outer loop, once in the inner for each check). 它保留了原始距离,这意味着min必须在两个检查中都是平方根(一次在外循环中,每次检查一次在内部)。

So, 所以,

if ((p.getX() - q.getX()) < min)

Should be 应该

if ((p.getX() - q.getX()) < Math.sqrt(min))

Same goes for the other check. 其他检查也是如此。

Thanks for your answers everyone! 谢谢大家的回答!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM