如何解决python中的Traveling Salesman问题?我没有找到任何库,应该有一种使用scipy函数进行优化的方法或其他库。
我的hacky-extremelly-lazy-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)是距离矩阵。
如果Dist太大,这将永远存在。
有什么建议吗?
最佳答案
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.
这是正在使用的函数的示例:
# Create a matrix of cities, with each row being a location in 2-space (function works in n-dimensions).
cities = np.random.RandomState(42).rand(70,2)
# Find a good route with 2-opt ("route" gives the order in which to travel to each city by row number.)
route = two_opt(cities,0.001)
这是图中显示的近似解路径:
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))])
保持原样,在
cities
中,起始城市被固定为第一个城市,而终止城市是可变的。要使结尾城市成为
cities
中的最后一个城市,请通过更改swap_first
中的swap_last
和two_opt()
的范围,以限制可交换城市的范围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)):
关于python - 偷偷摸摸的旅行推销员,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25585401/