使用pyconcorde计算二维平面的旅行商问题(TSP)

简介

众所周知,TSP问题是一个NP问题,想精确求得解需要指数级的计算量。即使使用分支限界方法等非近似算法算得依然很慢。在大部分情况只能考虑通过近似的方法来求解。比较好的近似算法有:

  • Lin–Kernighan 在100以内几乎可以得到最优解,速度很快,时间复杂度 O ( n 2.2 ) O(n^{2.2}) O(n2.2)
  • Christofides 理论最坏情况可以达到1.5倍的近似比,时间复杂度 O ( n 3 ) O(n^3) O(n3)

但是由William J. Cook等人实现的精确求解器Concorde,通过特殊的数值优化技巧却达到了很快的速度。规模几百左右的TSP问题都能够在很快求解。非常适用用来做学术研究。

本文对该求解器的python包装版本pyconcorde进行了安装和使用。记录了详细的配置步骤和实验结果。

安装环镜

语言环境:python 3.8

系统环境:ubuntu 20 x64 或 OSX 12.0.1 x64

安装依赖

git clone  https://github.com/jvkersch/pyconcorde
cd pyconcorde
pip install . -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple

使用示例

from concorde.tsp import TSPSolver
from typing import *
from random import random
import matplotlib.pyplot as plt
import numpy as np

def tsp(dots:List[Tuple[float, float]])->List[int]:
    """
    TSP最优算法(concorde)
    :param dots: 一系列的点的坐标,点之间的距离表示代价
    :return: 一系列点的编号,代表得到的哈密顿环
    """
    # solver only supports integral edge weights, so here float will be rounded to two
    # decimals, multiplied by 100 and converted to integer
    xs = []
    ys = []
    for (x, y) in dots:
        xs.append(int(x * 1000))
        ys.append(int(y * 1000))
    solver = TSPSolver.from_data(xs, ys, norm="EUC_2D")
    solution = solver.solve()
    #print(solution.found_tour)
    #print(solution.optimal_value)
    #print(solution.tour)
    return solution.tour.tolist()

class ConcordeTsp(object):
    """
    TSP算法封装类
    使用方法:ConcordeTsp(dots).tsp()
    其中,dots为二维平面上的点的列表
    """
    def __init__(self, dots:List[Tuple[float, float]]):
        # 去除重复的点
        dotSet = set()
        dotFilted = []
        for dot in dots:
            if not dot in dotSet:
                dotSet.add(dot)
                dotFilted.append(dot)
        self.__dots = dotFilted

    def tsp(self)->List[int]:
        """
        :return: tsp回路的近似解,返回结果不包含初始结点
        """
        return tsp(self.__dots)

if __name__ == "__main__":
    """以下是测试代码"""
    dots = []
    xs = []
    ys = []
    for i in range(51):
        x = random()
        y = random()
        dots.append((x, y))
        xs.append(x)
        ys.append(y)
    path = ConcordeTsp(dots).tsp()
    print(path)
    path.append(path[0])
    plt.scatter(np.array(xs)[path], np.array(ys)[path])
    for i in range(len(xs)):
        plt.annotate(str(path[i]),
                     xy=(xs[path[i]], ys[path[i]]),
                     xytext=(xs[path[i]] + 0.0001, ys[path[i]] + 0.0001))
        # 这里xy是需要标记的坐标,xytext是标签显示的位置
    plt.plot(np.array(xs)[path], np.array(ys)[path])
    plt.show()

请查看 concordewapper.py 中的测试代码。下面是一个规模为51的欧式空间TSP问题的运行结果
使用pyconcorde计算二维平面的旅行商问题(TSP)_第1张图片

注意

  • OSX 系统需要在pyconcorde中的setup.py开头处添加以下代码(为了跳过SSL验证,否则安装pyconcorde会报错)。
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
  • 本项目使用的是pyconcorde版本是 0.1.0

你可能感兴趣的:(算法,python,TSP,Concorde)