代码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.py
和 DW.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 #用来正常显示负号
跑一下试试: