最近在学习算法设计这门课,老师在讲遗传算法解决旅行商问题,所以找了些资料学习下,也算是自己转专业来一个入门的实践,先把我资料的网址贴上:https://www.bilibili/video/BV17Z4y1w7qF?from=search&seid=4612262847067450332&spm_id_from=333.337.0.0https://www.bilibili/video/BV17Z4y1w7qF?from=search&seid=4612262847067450332&spm_id_from=333.337.0.0
另外,我所用的数据: 我用的是Berlin52http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/旅行商问题表示一个人要不重复的走完地图中所有的点在返回原地,怎样距离最短?
上代码
首先配置文件:表示的是一些默认的数值,这里我设置每一代个体30,总共2000代,52则表示城市的数量
# -*- coding: utf-8 -*-
import argparse
parser = argparse.ArgumentParser(description='Configuration file')
arg_lists = []
def add_argument_group(name):
arg = parser.add_argument_group(name)
arg_lists.append(arg)
return arg
# Data
data_arg = add_argument_group('Data')
data_arg.add_argument('--city_num', type=int, default=52, help='city num') # 城市数量
data_arg.add_argument('--pos_dimension', type=int, default=2, help='city num') # 坐标维度
data_arg.add_argument('--individual_num', type=int, default=30, help='individual num') # 个体数
data_arg.add_argument('--gen_num', type=int, default=2000, help='generation num') # 迭代轮数
data_arg.add_argument('--mutate_prob', type=float, default=0.3, help='probability of mutate') # 变异概率
def get_config():
config, unparsed = parser.parse_known_args()
return config
def print_config():
config = get_config()
print('\n')
print('Data Config:')
print('* city num:', config.city_num)
print('* individual num:', config.individual_num)
print('* generation num:', config.gen_num)
print('* probability of mutate:', config.mutate_prob)
主文件:
import math
import numpy as np
import config as conf
from ga import Ga
import matplotlib.pyplot as plt
config = conf.get_config()
def read_tsp(path): #打开tsp文件
lines = open(path, 'r').readlines()
assert 'NODE_COORD_SECTION\n' in lines
index = lines.index('NODE_COORD_SECTION\n')
data = lines[index + 1:-1]
tmp = []
for line in data:
line = line.strip().split(' ')
if line[0] == 'EOF':
continue
tmpline = []
for x in line:
if x == '':
continue
else:
tmpline.append(float(x))
if tmpline == []:
continue
tmp.append(tmpline)
data = tmp
data = np.array(data)
data = data[:, 1:]
data = np.array(data)
return data
def build_dist_mat(input_list):
n,m=input_list.shape
dist_mat = np.zeros([n, n])
for i in range(n):
for j in range(i + 1, n):
d = input_list[i, :] - input_list[j, :]
# 计算点积
dist_mat[i, j] =math.sqrt(np.dot(d, d))
dist_mat[j, i] = dist_mat[i, j]
return dist_mat
# 城市坐标
city_pos_list = read_tsp(r"C:\Users\XXX\OneDrive\桌面\资料\上课\berlin52.tsp")
# 城市距离矩阵
city_dist_mat = build_dist_mat(city_pos_list)
#
print(city_pos_list)
print(city_dist_mat)
# 遗传算法运行
ga = Ga(city_dist_mat)
result_list, fitness_list = ga.train()
result = result_list[-1]
result_pos_list = city_pos_list[result, :]
# 绘图
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
fig = plt.figure()
plt.plot(result_pos_list[:, 0], result_pos_list[:, 1], 'o-r')
plt.title(u"路线")
plt.legend()
fig.show()
fig = plt.figure()
plt.plot(fitness_list)
plt.title(u"适应度曲线")
plt.legend()
fig.show()
print(fitness_list[-1])
最主要的算法文件:
import config as conf
import random
city_dist_mat = None
config = conf.get_config()
# 各项参数
gene_len = config.city_num
individual_num = config.individual_num
gen_num = config.gen_num
mutate_prob = config.mutate_prob
def copy_list(old_arr: [int]):
new_arr = []
for element in old_arr:
new_arr.append(element)
return new_arr
# 个体类
class Individual:
def __init__(self, genes=None):
# 随机生成序列
if genes is None:
genes = [i for i in range(gene_len)]
random.shuffle(genes)
self.genes = genes
self.fitness = self.evaluate_fitness() #随机生成初始单个基因
def evaluate_fitness(self):
# 计算个体适应度
fitness = 0.0
for i in range(gene_len - 1):
# 起始城市和目标城市
from_idx = self.genes[i]
to_idx = self.genes[i + 1]
fitness += city_dist_mat[from_idx, to_idx]
# 连接首尾
fitness += city_dist_mat[self.genes[-1], self.genes[0]]
return fitness #计算路程距离
class Ga: #基因函数 很重要
def __init__(self, input_):
global city_dist_mat #全局变量 在模块内、在所有函数外面、在class外面,这就是全局变量。
city_dist_mat = input_ #将输入数据转化为距离矩阵
self.best = None # 每一代的最佳个体
self.individual_list = [] # 所有个体
self.result_list = [] # 路线结果
self.fitness_list = [] # 每一代对应的适应度
def cross(self):
new_gen = []
random.shuffle(self.individual_list) #打乱所有基因个体列表
for i in range(0, individual_num - 1, 2):
# 父代基因
genes1 = copy_list(self.individual_list[i].genes) #父亲基因
genes2 = copy_list(self.individual_list[i + 1].genes)#母亲基因
index1 = random.randint(0, gene_len - 2) #随机生成基因index 保证index1小于index2 [index1,index2]代表交叉过程中的基因片段
index2 = random.randint(index1, gene_len - 1) #
pos1_recorder = {value: idx for idx, value in enumerate(genes1)} #记录初始基因中每个点的位置到字典中
pos2_recorder = {value: idx for idx, value in enumerate(genes2)}
# 交叉
for j in range(index1, index2): #此处方法非常巧妙 建议61-70行单独拿出逐行分析,将j的取值范围从小变大挨个分析。
value1, value2 = genes1[j], genes2[j]
pos1, pos2 = pos1_recorder[value2], pos2_recorder[value1]
genes1[j], genes1[pos1] = genes1[pos1], genes1[j]
genes2[j], genes2[pos2] = genes2[pos2], genes2[j]
pos1_recorder[value1], pos1_recorder[value2] = pos1, j
pos2_recorder[value1], pos2_recorder[value2] = j, pos2
new_gen.append(Individual(genes1)) #生成个体1和2
new_gen.append(Individual(genes2))
return new_gen
def mutate(self, new_gen):
for individual in new_gen:
if random.random() < mutate_prob:
# 翻转切片
old_genes = copy_list(individual.genes)
index1 = random.randint(0, gene_len - 2)
index2 = random.randint(index1, gene_len - 1)
genes_mutate = old_genes[index1:index2]
genes_mutate.reverse()
individual.genes = old_genes[:index1] + genes_mutate + old_genes[index2:]
# 两代合并
self.individual_list += new_gen
def select(self):
# 锦标赛
group_num =10 # 小组数
group_size = 20 # 每小组人数
group_winner = individual_num // group_num # 每小组获胜人数
winners = [] # 锦标赛结果
for i in range(group_num):
group = []
for j in range(group_size):
# 随机组成小组
player = random.choice(self.individual_list)
player = Individual(player.genes)
group.append(player)
group = Ga.rank(group)
# 取出获胜者
winners += group[:group_winner]
self.individual_list = winners
@staticmethod
def rank(group):
# 冒泡排序
for i in range(1, len(group)):
for j in range(0, len(group) - i):
if group[j].fitness > group[j + 1].fitness:
group[j], group[j + 1] = group[j + 1], group[j]
return group
def next_gen(self):
# 交叉
new_gen = self.cross()
# 变异
self.mutate(new_gen)
# 选择
self.select()
# 获得这一代的结果
for individual in self.individual_list:
if individual.fitness < self.best.fitness:
self.best = individual
def train(self):
# 初代种群
self.individual_list = [Individual() for _ in range(individual_num)]
self.best = self.individual_list[0]
# 迭代
for i in range(gen_num):
self.next_gen()
# 连接首尾
result = copy_list(self.best.genes)
result.append(result[0])
self.result_list.append(result)
self.fitness_list.append(self.best.fitness)
return self.result_list, self.fitness_list
这个Berlin52的最优解7542
但是我最好也就是7800左右
更多推荐
遗传算法解决TSP(旅行商问题)
发布评论