2-opt算法解决Python中的旅行商问题

时间:2018-11-13 06:48:50

标签: python python-3.x algorithm adjacency-matrix traveling-salesman

我在Python中找不到2-opt算法的任何完整实现,因此我试图将缺少的部分添加到找到的here的代码中,下面将对此进行介绍。

def two_opt(route):
     best = route
     improved = True
     while improved:
          improved = False
          for i in range(1, len(route)-2):
               for j in range(i+1, len(route)):
                    if j-i == 1: continue # changes nothing, skip then
                    new_route = route[:]
                    new_route[i:j] = route[j-1:i-1:-1] # this is the 2woptSwap
                    if cost(new_route) < cost(best):  # what should cost be?
                         best = new_route
                         improved = True
          route = best
     return best

为了完成此代码,我制作了一个小程序,从文本文件中提取长/纬度坐标,并用每个点的成本填充邻接矩阵。完整的代码,包括输入坐标和邻接矩阵的样本,可以在Code Review上找到。

由于我不知道上面的代码中的cost函数是什么,所以我的想法是计算出从一个点到另一个点的所有成本,并将其放在邻接矩阵:adj_matrix中。这表示每个点与其他点的距离。

我尝试将成本/邻接矩阵传递给该函数以使用该函数,但是鉴于邻接矩阵,我无法计算成本。

def main():
    # code to read from file
    # code to append co-ordinates to points and calculate the haversine distance between each point
    route = random.sample(range(10), 10)
    best = two_opt(route, adj_matrix)  # passing my adjacency matrix
    print(best)
  

ValueError:具有多个元素的数组的真值不明确。使用a.any()或a.all()

另一个Python 2选项问题:Generate all neighbors for 2OPT in python

任何有关如何从邻接矩阵中找到正确成本的建议。

3 个答案:

答案 0 :(得分:1)

首先,一个adjacency matrix is typically a (0, 1)-matrix。在这里,您所拥有的被称为 cost weight 距离矩阵

现在是您的问题。

成本函数可以很简单:

def cost(cost_mat, route):
   return cost_mat[np.roll(route, 1), route].sum()

在这里,np.roll()将路线“旋转”一个位置,以使其易于与route一起使用,以建立成本矩阵索引。 sum()只需将各个路段的费用相加即可计算出路线的总费用。

(如果您决定查看非对称TSP,则需要确保行/列顺序与cost_mat的构造方式匹配;对于欧氏TSP,这与成本矩阵是对称的。)

使用示例:

cost_mat = np.array([
   [0, 1, 2, 3],
   [1, 0, 4, 5],
   [2, 4, 0, 7],
   [3, 5, 7, 0],
])

route = np.array([2, 1, 3, 0])

print(cost(cost_mat, route))

答案 1 :(得分:0)

2-opt删除两个边并创建两个新边(假设成本矩阵是对称的),因此可以简化成本函数以仅考虑变化的边。对于大型阵列,这比枚举整个路径要快得多。

import numpy as np

def cost_change(cost_mat, n1, n2, n3, n4):
    return cost_mat[n1][n3] + cost_mat[n2][n4] - cost_mat[n1][n2] - cost_mat[n3][n4]


def two_opt(route, cost_mat):
    best = route
    improved = True
    while improved:
        improved = False
        for i in range(1, len(route) - 2):
            for j in range(i + 1, len(route)):
                if j - i == 1: continue
                if cost_change(cost_mat, best[i - 1], best[i], best[j - 1], best[j]) < 0:
                    best[i:j] = best[j - 1:i - 1:-1]
                    improved = True
        route = best
    return best


if __name__ == '__main__':
    nodes = 1000
    init_route = list(range(nodes))
    print(init_route)
    cost_mat = np.random.randint(100, size=(nodes, nodes))
    cost_mat += cost_mat.T
    np.fill_diagonal(cost_mat, 0)
    cost_mat = list(cost_mat)
    best_route = two_opt(init_route, cost_mat)
    print(best_route)

答案 2 :(得分:0)

我在 Python 中实现了 2-opt 算法。您可以使用 pip install py2opt 从 PyPi 服务器安装它。您还可以找到它的实现 here。使用此包,您可以计算最短距离(虽然不是全局最小值)和最佳对应路线。

这是如何在几行中使用此库的示例。

from py2opt.routefinder import RouteFinder

cities_names = ['A', 'B', 'C', 'D']
dist_mat = [[0, 29, 15, 35], [29, 0, 57, 42], [15, 57, 0, 61], [35, 42, 61, 0]]
route_finder = RouteFinder(dist_mat, cities_names, iterations=5)
best_distance, best_route = route_finder.solve()

print(best_distance)
114
print(best_route)
['A', 'C', 'B', 'D']

我在这篇文章中对其进行了更详细的解释:How to Solve the Traveling Salesman Problem — A Comparative Analysis