纯Python实现TSP问题求解,输入一组坐标文件,输出根据最佳路径重新排序的坐标文件

代码A

基于python-tsp(https://github.com/fillipe-gsm/python-tsp/)实现

# -*- encoding: utf-8 -*-
#基于 python-tsp
#https://github.com/fillipe-gsm/python-tsp/
#输入:带有2列数据的csv,第一列为x值,第二列为y值 position_xy_input.csv
#输出:带有2列数据的csv,第一列为x值,第二列为y值 position_xy_output.csv

import csv
import numpy as np
from python_tsp.distances import great_circle_distance_matrix
from python_tsp.exact import solve_tsp_dynamic_programming
from python_tsp.heuristics import solve_tsp_local_search, solve_tsp_simulated_annealing

def create_output_file(filename,data,indexes):
    x = [data[i][0] for i in indexes]
    y = [data[i][1] for i in indexes]
    with open(filename,'w',newline='')as f:
        writer = csv.writer(f)
        for pos in zip(x, y):
            writer.writerow(pos)

sources = np.genfromtxt('position_xy_input.csv',delimiter=',') 
distance_matrix = great_circle_distance_matrix(sources) #获得距离矩阵
distance_matrix[:,0]=0  #不需要回到起点,所以将距离矩阵第一列所有元素设为零

permutation, distance = solve_tsp_simulated_annealing(distance_matrix)
permutation2, distance2 = solve_tsp_local_search(
    distance_matrix, x0=permutation, perturbation_scheme="ps3"
)

oridata = sources.tolist()
create_output_file('position_xy_output.csv',oridata,permutation2)


代码B

参考https://github.com/425776024/TSP-GA.py
根据实际需求稍微改写了下TSP_GA.pyDW.py 两个文件

输入文件:pos_input.csv
输出文件:pos_output.csv

TSP_M.py

# -*- encoding: utf-8 -*-
#基于https://github.com/425776024/TSP-GA-py修改
#输入:带有2列数据的csv,第一列为x值,第二列为y值 position_xy_input.csv
#输出:带有2列数据的csv,第一列为x值,第二列为y值 position_xy_output.csv

import csv
import numpy as np
import pandas as pd
from numpy.linalg import norm
#windows
# from . import Draw

#linux
from DW_M import Draw

class TSP(object):
    poss = np.array([])
    pos_list = np.array([])
    pop_size = 50
    c_rate = 0.7
    m_rate = 0.05
    pop = np.array([])
    fitness = np.array([])
    pos_size = -1
    ga_num = 500
    best_dist = 1
    best_gen = []
    dw = Draw()

    def __init__(self, c_rate, m_rate, pop_size, ga_num):
        self.fitness = np.zeros(self.pop_size)
        self.c_rate = c_rate
        self.m_rate = m_rate
        self.pop_size = pop_size
        self.ga_num = ga_num

    def init(self):
        tsp = self
        tsp.load_Position()
        tsp.pop = tsp.creat_pop(tsp.pop_size)
        tsp.fitness = tsp.get_fitness(tsp.pop)
        tsp.dw.bound_x = [np.min(tsp.poss[:, 0]), np.max(tsp.poss[:, 0])]
        tsp.dw.bound_y = [np.min(tsp.poss[:, 1]), np.max(tsp.poss[:, 1])]
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)

    # --------------------------------------
    def creat_pop(self, size):
        pop = []
        for i in range(size):
            gene = np.arange(self.poss.shape[0])
            np.random.shuffle(gene)
            pop.append(gene)

        return np.array(pop)

    def get_fitness(self, pop):
        d = np.array([])
        for i in range(pop.shape[0]):
            gen = pop[i]  # 取其中一条染色体,编码解
            dis = self.gen_distance(gen)
            dis = self.best_dist / dis
            d = np.append(d, dis)  # 求路径长
        return d

    def get_local_fitness(self, gen, i):
        '''
        :param gen:坐标点路径
        :param i:第i坐标点
        :return:第i坐标点的局部适应度
        '''
        di = 0
        fi = 0
        if i == 0:
            di = self.ct_distance(self.poss[gen[0]], self.poss[gen[-1]])
        else:
            di = self.ct_distance(self.poss[gen[i]], self.poss[gen[i - 1]])
        od = []
        for j in range(self.pos_size):
            if i != j:
                od.append(self.ct_distance(self.poss[gen[i]], self.poss[gen[i - 1]]))
        mind = np.min(od)
        fi = di - mind
        return fi

    def EO(self, gen):
        local_fitness = []
        for g in range(self.pos_size):
            f = self.get_local_fitness(gen, g)
            local_fitness.append(f)
        max_pos_i = np.argmax(local_fitness)
        maxgen = np.copy(gen)
        if 1 < max_pos_i < self.pos_size - 1:
            for j in range(max_pos_i):
                maxgen = np.copy(gen)
                jj = max_pos_i
                while jj < self.pos_size:
                    gen1 = self.exechange_gen(maxgen, j, jj)
                    d = self.gen_distance(maxgen)
                    d1 = self.gen_distance(gen1)
                    if d > d1:
                        maxgen = gen1[:]
                    jj += 1
        gen = maxgen
        return gen

    # -------------------------------------
    def select_pop(self, pop):
        best_f_index = np.argmax(self.fitness)
        av = np.median(self.fitness, axis=0)
        for i in range(self.pop_size):
            if i != best_f_index and self.fitness[i] < av:
                pi = self.cross(pop[best_f_index], pop[i])
                pi = self.mutate(pi)
                pop[i, :] = pi[:]

        return pop

    def select_pop2(self, pop):
        probility = self.fitness / self.fitness.sum()
        idx = np.random.choice(np.arange(self.pop_size), size=self.pop_size, replace=True, p=probility)
        n_pop = pop[idx, :]
        return n_pop

    def cross(self, parent1, parent2):
        """交叉"""
        if np.random.rand() > self.c_rate:
            return parent1
        index1 = np.random.randint(0, self.pos_size - 1)
        index2 = np.random.randint(index1, self.pos_size - 1)
        tempGene = parent2[index1:index2]  # 交叉的基因片段
        newGene = []
        p1len = 0
        for g in parent1:
            if p1len == index1:
                newGene.extend(tempGene)  # 插入基因片段
            if g not in tempGene:
                newGene.append(g)
            p1len += 1
        newGene = np.array(newGene)

        if newGene.shape[0] != self.pos_size:
            print('c error')
            return self.creat_pop(1)
        return newGene

    def mutate(self, gene):
        """突变"""
        if np.random.rand() > self.m_rate:
            return gene
        index1 = np.random.randint(0, self.pos_size - 1)
        index2 = np.random.randint(index1, self.pos_size - 1)
        newGene = self.reverse_gen(gene, index1, index2)
        if newGene.shape[0] != self.pos_size:
            print('m error')
            return self.creat_pop(1)
        return newGene

    def reverse_gen(self, gen, i, j):
        if i >= j:
            return gen
        if j > self.pos_size - 1:
            return gen
        parent1 = np.copy(gen)
        tempGene = parent1[i:j]
        newGene = []
        p1len = 0
        for g in parent1:
            if p1len == i:
                newGene.extend(tempGene[::-1])  # 插入基因片段
            if g not in tempGene:
                newGene.append(g)
            p1len += 1
        return np.array(newGene)

    def exechange_gen(self, gen, i, j):
        c = gen[j]
        gen[j] = gen[i]
        gen[i] = c
        return gen

    def evolution(self):
        tsp = self
        for i in range(self.ga_num):
            best_f_index = np.argmax(tsp.fitness)
            worst_f_index = np.argmin(tsp.fitness)
            local_best_gen = tsp.pop[best_f_index]
            local_best_dist = tsp.gen_distance(local_best_gen)
            if i == 0:
                tsp.best_gen = local_best_gen
                tsp.best_dist = tsp.gen_distance(local_best_gen)

            if local_best_dist < tsp.best_dist:
                tsp.best_dist = local_best_dist
                tsp.best_gen = local_best_gen
            else:
                tsp.pop[worst_f_index] = self.best_gen
            print('gen:%d evo,best dist :%s' % (i, self.best_dist), end='\r')

            tsp.pop = tsp.select_pop(tsp.pop)
            tsp.fitness = tsp.get_fitness(tsp.pop)
            for j in range(self.pop_size):
                r = np.random.randint(0, self.pop_size - 1)
                if j != r:
                    tsp.pop[j] = tsp.cross(tsp.pop[j], tsp.pop[r])
                    tsp.pop[j] = tsp.mutate(tsp.pop[j])
            tsp.best_dist = tsp.gen_distance(self.best_gen)

    def gen_distance(self, gen):
        distance = 0.0
        for i in range(-1, len(self.poss) - 1):
            index1, index2 = gen[i], gen[i + 1]
            distance += norm(self.poss[index1]-self.poss[index2])
        return distance

    def ct_distance(self, pos1, pos2):
        return norm(pos1 - pos2)

    def draw_poss_way(self, gen):
        '''
        根据一条基因gen绘制一条行进路线
        :param gen:
        :return:
        '''
        tsp = self
        dw = self.dw
        m = gen.shape[0]
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)
        for i in range(m):
            if i < m - 1:
                best_i = tsp.best_gen[i]
                next_best_i = tsp.best_gen[i + 1]
                best_ipos = tsp.poss[best_i]
                next_best_ipos = tsp.poss[next_best_i]
                dw.draw_line(best_ipos, next_best_ipos)
        start = tsp.poss[tsp.best_gen[0]]
        end = tsp.poss[tsp.best_gen[-1]]
        dw.draw_line(end, start)

    def draw_map(self, gen, size=5):
        '''
        根据一条基因gen绘制对应坐标点名称
        :param gen:
        :param size: text size
        :return:
        '''
        tsp = self
        m = gen.shape[0]
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)
        for i in range(m):
            c = gen[i]
            best_ipos = tsp.poss[c]
            tsp.dw.draw_text(best_ipos[0], best_ipos[1], None, 10)

    def re_draw(self):
        tsp = self
        tsp.dw.draw_points(tsp.poss[:, 0], tsp.poss[:, 1])
        tsp.draw_map(tsp.pop[0], 8)
        tsp.draw_poss_way(self.best_gen)

    def load_Position(self,file='position_xy_input.csv', delm=','):
        data = pd.read_csv(file, delimiter=delm, header=None).values
        self.poss = data[:, 0:]/1000.
        self.pos_size = data.shape[0]
        self.pos_list = data

    def get_best_result(self):
        print("最佳行进顺序:",self.best_gen) #np.array

    def create_output_file(self,filename):
        data = self.pos_list.tolist()
        indexes = self.best_gen.tolist()
        x = [data[i][0] for i in indexes]
        y = [data[i][1] for i in indexes]
        with open(filename,'w',newline='')as f:
            writer = csv.writer(f)
            for pos in zip(x, y):
                writer.writerow(pos)

def main():
    tsp = TSP(0.5, 0.1, 100, 2000) #参数根据实际需求去调整
    tsp.init()
    tsp.evolution()
    tsp.re_draw()
    tsp.get_best_result()
    tsp.create_output_file('position_xy_output.csv')
    tsp.dw.plt.show()

if __name__ == '__main__':
    main()

DW_M.py

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

class Draw(object):
    bound_x = []
    bound_y = []

    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.plt = plt
        self.set_display()

    def draw_line(self, p_from, p_to):
        line1 = [(p_from[0], p_from[1]), (p_to[0], p_to[1])]
        (line1_xs, line1_ys) = zip(*line1)
        self.ax.add_line(Line2D(line1_xs, line1_ys, linewidth=1, color='blue'))

    def draw_points(self, pointx, pointy):
        self.ax.plot(pointx, pointy, 'ro')

    def set_xybound(self, x_bd, y_bd):
        self.ax.axis([x_bd[0], x_bd[1], y_bd[0], y_bd[1]])

    def draw_text(self, x, y, text, size=8):
        self.ax.text(x, y, text, fontsize=size)

    def set_display(self, ft_style='Ubuntu'):
        #linux 下可能会因为SimHei字体缺失报错,需要自行去添加字体和修改 matplotlib
        #可以换成别的字体 例如 ft_style='SimHei'  -> ft_style='Ubuntu'
        plt.rcParams['font.sans-serif'] = [ft_style]  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

跑一下试试:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值