通过点集合中的所有其他点,两点之间的最短路径

时间:2014-05-09 19:12:35

标签: c# algorithm geometry distance

给定由x和y坐标定义的点集合。

在这个集合中,我得到起点,终点和所有其他n-2点。

我必须通过所有其他点找到起点和终点之间的最短路径。最短的方式由其值定义,如果可能的话,定义交叉点顺序。

初看起来这似乎是一个图形问题,但我现在对此不太确定,无论如何我只想使用几何关系来找到这种最短路径,因为目前所有的信息都是只有点的x和y坐标,哪个点是起点,哪个是终点。

我的问题是,通过仅使用几何关系可以找到这种方式吗?

我正在尝试在C#中实现这一点,所以如果有一些有用的软件包,请告诉我。

4 个答案:

答案 0 :(得分:4)

Euclidean travelling salesman问题可以归结为此问题,并且它是NP难题。因此,除非你的点集很小或者你有一个非常特殊的结构,你应该注意一个近似值。请注意,维基百科文章提到了问题的存在PTAS,这可能会在实践中变得非常有效。

更新:由于您的实例似乎只有很少的节点,因此您可以使用简单的指数时动态编程方法。设f(S,p)是连接集合S中所有点的最小成本,以点p结束。我们有f({start},start)= 0,我们正在寻找f(P,end),其中P是所有点的集合。为了计算f(S,p),我们可以在巡回赛中检查p的所有潜在前辈,所以我们有

f(S, p) = MIN(q in S \ {p}, f(S \ {p}, q) + distance(p, q))

您可以将S表示为位向量来节省空间(为了最大限度地简化,只需使用单字整数)。还可以使用memoization来避免重新计算子问题结果。

运行时将为O(2 ^ n * n ^ 2)并且算法可以用相当低的常数因子实现,所以我预测它能够在 seconds 25内解决n = 25的实例/ strike>合理的时间。

答案 1 :(得分:4)

具有合理性能的最简单的启发式是2-opt。将点放在一个数组中,起始点为第一个,结束点为最后一个,并反复尝试按如下方式改进解决方案。选择起始索引i和结束索引j并将子阵列从i反转为j。如果总成本较低,则保留此更改,否则撤消它。注意,当且仅当d(p [i-1],p [i])+ d(p [j],p [j + 1])> 0时,总成本将更少。 d(p [i - 1],p [j])+ d(p [i],p [j + 1]),所以你可以避免执行交换,除非它是一个改进。

此方法可能有许多改进。 3-opt和k-opt考虑更多可能的移动,从而提高解决方案质量。例如,几何搜索的数据结构,例如kd树,减少了找到改进移动的时间。据我所知,TSP的本地搜索算法的最新技术是Keld Helsgaun的LKH

另一类算法是分支和绑定。这些返回最佳解决方案Concorde(据我所知)是这里的最新技术。

这是Niklas描述的O(n ^ 2 2 ^ n)DP的Java实现。有许多可能的改进,例如,缓存点之间的距离,切换到浮点(可能),重新组织迭代,以便子集以递增的大小顺序枚举(仅允许最新的minTable层保留,节省了大量空间。)

class Point {
    private final double x, y;

    Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    double distanceTo(Point that) {
        return Math.hypot(x - that.x, y - that.y);
    }

    public String toString() {
        return x + " " + y;
    }
}

public class TSP {
    public static int[] minLengthPath(Point[] points) {
        if (points.length < 2) {
            throw new IllegalArgumentException();
        }
        int n = points.length - 2;
        if ((1 << n) <= 0) {
            throw new IllegalArgumentException();
        }
        byte[][] argMinTable = new byte[1 << n][n];
        double[][] minTable = new double[1 << n][n];
        for (int s = 0; s < (1 << n); s++) {
            for (int i = 0; i < n; i++) {
                int sMinusI = s & ~(1 << i);
                if (sMinusI == s) {
                    continue;
                }
                int argMin = -1;
                double min = points[0].distanceTo(points[1 + i]);
                for (int j = 0; j < n; j++) {
                    if ((sMinusI & (1 << j)) == 0) {
                        continue;
                    }
                    double cost =
                        minTable[sMinusI][j] +
                        points[1 + j].distanceTo(points[1 + i]);
                    if (argMin < 0 || cost < min) {
                        argMin = j;
                        min = cost;
                    }
                }
                argMinTable[s][i] = (byte)argMin;
                minTable[s][i] = min;
            }
        }
        int s = (1 << n) - 1;
        int argMin = -1;
        double min = points[0].distanceTo(points[1 + n]);
        for (int i = 0; i < n; i++) {
            double cost =
                minTable[s][i] +
                points[1 + i].distanceTo(points[1 + n]);
            if (argMin < 0 || cost < min) {
                argMin = i;
                min = cost;
            }
        }
        int[] path = new int[1 + n + 1];
        path[1 + n] = 1 + n;
        int k = n;
        while (argMin >= 0) {
            path[k] = 1 + argMin;
            k--;
            int temp = s;
            s &= ~(1 << argMin);
            argMin = argMinTable[temp][argMin];
        }
        path[0] = 0;
        return path;
    }

    public static void main(String[] args) {
        Point[] points = new Point[20];
        for (int i = 0; i < points.length; i++) {
            points[i] = new Point(Math.random(), Math.random());
        }
        int[] path = minLengthPath(points);
        for (int i = 0; i < points.length; i++) {
            System.out.println(points[path[i]]);
            System.err.println(points[i]);
        }
    }
}

答案 2 :(得分:0)

这可以使用进化算法来解决。 看看这个:http://johnnewcombe.net/blog/post/23

答案 3 :(得分:0)

您可能希望查看TPL(任务并行库)以加速应用程序。

修改

我发现这个链接有一个旅行推销员算法: http://msdn.microsoft.com/en-us/magazine/gg983491.aspx

据说源代码位于: http://archive.msdn.microsoft.com/mag201104BeeColony