给定由x和y坐标定义的点集合。
在这个集合中,我得到起点,终点和所有其他n-2点。
我必须通过所有其他点找到起点和终点之间的最短路径。最短的方式由其值定义,如果可能的话,定义交叉点顺序。
初看起来这似乎是一个图形问题,但我现在对此不太确定,无论如何我只想使用几何关系来找到这种最短路径,因为目前所有的信息都是只有点的x和y坐标,哪个点是起点,哪个是终点。
我的问题是,通过仅使用几何关系可以找到这种方式吗?
我正在尝试在C#中实现这一点,所以如果有一些有用的软件包,请告诉我。
答案 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
答案 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