Python 西皮的旅行推销员

Python 西皮的旅行推销员,python,optimization,scipy,traveling-salesman,Python,Optimization,Scipy,Traveling Salesman,如何用python解决旅行推销员问题?我没有找到任何库,应该有一种使用scipy函数进行优化或其他库的方法 我的黑客极端懒惰的pythonic暴力解决方案是: tsp_solution = min( (sum( Dist[i] for i in izip(per, per[1:])), n, per) for n, per in enumerate(i for i in permutations(xrange(Dist.shape[0]), Dist.shape[0])) )[2] 其中Dis

如何用python解决旅行推销员问题?我没有找到任何库,应该有一种使用scipy函数进行优化或其他库的方法

我的黑客极端懒惰的pythonic暴力解决方案是:

tsp_solution = min( (sum( Dist[i] for i in izip(per, per[1:])), n, per) for n, per in enumerate(i for i in permutations(xrange(Dist.shape[0]), Dist.shape[0])) )[2]
其中Dist(numpy.array)是距离矩阵。 如果距离太大,这将花费很长时间


建议?

构造的
scipy.optimize
函数不允许直接适应旅行商问题(TSP)。对于一个简单的解决方案,我建议使用2-opt算法,这是一个被广泛接受的解决TSP的算法,并且实现起来相对简单。以下是我对算法的实现:

import numpy as np

# Calculate the euclidian distance in n-space of the route r traversing cities c, ending at the path start.
path_distance = lambda r,c: np.sum([np.linalg.norm(c[r[p]]-c[r[p-1]]) for p in range(len(r))])
# Reverse the order of all elements from element i to element k in array r.
two_opt_swap = lambda r,i,k: np.concatenate((r[0:i],r[k:-len(r)+i-1:-1],r[k+1:len(r)]))

def two_opt(cities,improvement_threshold): # 2-opt Algorithm adapted from https://en.wikipedia.org/wiki/2-opt
    route = np.arange(cities.shape[0]) # Make an array of row numbers corresponding to cities.
    improvement_factor = 1 # Initialize the improvement factor.
    best_distance = path_distance(route,cities) # Calculate the distance of the initial path.
    while improvement_factor > improvement_threshold: # If the route is still improving, keep going!
        distance_to_beat = best_distance # Record the distance at the beginning of the loop.
        for swap_first in range(1,len(route)-2): # From each city except the first and last,
            for swap_last in range(swap_first+1,len(route)): # to each of the cities following,
                new_route = two_opt_swap(route,swap_first,swap_last) # try reversing the order of these cities
                new_distance = path_distance(new_route,cities) # and check the total distance with this modification.
                if new_distance < best_distance: # If the path distance is an improvement,
                    route = new_route # make this the accepted best route
                    best_distance = new_distance # and update the distance corresponding to this route.
        improvement_factor = 1 - best_distance/distance_to_beat # Calculate how much the route has improved.
    return route # When the route is no longer improving substantially, stop searching and return the route.
这是图上显示的近似解路径:

import matplotlib.pyplot as plt
# Reorder the cities matrix by route order in a new matrix for plotting.
new_cities_order = np.concatenate((np.array([cities[route[i]] for i in range(len(route))]),np.array([cities[0]])))
# Plot the cities.
plt.scatter(cities[:,0],cities[:,1])
# Plot the path.
plt.plot(new_cities_order[:,0],new_cities_order[:,1])
plt.show()
# Print the route as row numbers and the total distance travelled by the path.
print("Route: " + str(route) + "\n\nDistance: " + str(path_distance(route,cities)))

如果算法的速度对您很重要,我建议您预先计算距离并将其存储在矩阵中。这大大缩短了收敛时间

编辑:自定义起点和终点

对于非圆形路径(结束位置与其起始位置不同的路径),请将路径距离公式编辑为

path_distance = lambda r,c: np.sum([np.linalg.norm(c[r[p+1]]-c[r[p]]) for p in range(len(r)-1)])
然后重新排列城市,以便使用

new_cities_order = np.array([cities[route[i]] for i in range(len(route))])
代码不变时,起始城市固定为
城市
中的第一个城市,而结束城市是可变的

要使结束城市成为
城市中的最后一个城市
,请使用代码更改
two_opt()
中的
swap_first
swap_last
的范围,以限制可交换城市的范围

for swap_first in range(1,len(route)-3):
    for swap_last in range(swap_first+1,len(route)-1):
要使起始城市和结束城市都变为变量,请使用扩展
swap_first
swap_last
的范围

for swap_first in range(0,len(route)-2):
    for swap_last in range(swap_first+1,len(route)):

你说“解决”是什么意思?要为大量城市找到一条最短路线,除了严格检查组合之外,人类实际上并不知道该如何做,这是一件非常困难的事情。接近最优的解决方案可以吗?我们可以把城市数量限制在@BKay吗当然可以,早在2006年,一个有85900个城市的例子就解决了。我向你保证这不是暴力造成的。一般来说,我们遇到麻烦是因为它是NP完全的,但这并不意味着我们不能聪明处理它。我知道这在很多城市是无法解决的。我只想要一个最先进的启发式解决方案。(或者对数量较少的城市采用更明智的确定性方法)没错。我说得太快了。有相当好的近似解,速度很快,有些复杂,但比蛮力法快得多。我真的只是想知道你想要什么。Lin-Kernighan启发法非常有效。如果你想寻求实际的最优解,你可以看看基于线性规划的解算器。通过改变路径距离,这种实现对非圆形路径有效吗?(例如,使城市穿越不必在路径起点处结束)@Giolelm是的,确实如此。我添加了关于如何更改非圆形路径的路径距离的说明。对于非圆形路径,您可能还需要自定义起点和终点,因此我还添加了一个关于如何自定义这些点的说明。注意,我对
two\u opt\u swap
做了一点修改,以适应可变的起始城市。这对不对称TSP问题有效吗?我尝试了一个不对称的
path\u distance
函数,但我真的不知道2-opt是否适用于不对称情况:?@jjmontes这似乎对不对称问题有效,尽管我不知道它与其他启发式方法相比会如何。我很想知道它的性能如何。此外,我建议创建一个预先计算的距离表。它将大大提高对称和不对称情况下的性能。
for swap_first in range(0,len(route)-2):
    for swap_last in range(swap_first+1,len(route)):