遗传算法解决TSP(旅行商问题)

最近在学习算法设计这门课,老师在讲遗传算法解决旅行商问题,所以找了些资料学习下,也算是自己转专业来一个入门的实践,先把我资料的网址贴上:https://www.bilibili.com/video/BV17Z4y1w7qF?from=search&seid=4612262847067450332&spm_id_from=333.337.0.0icon-default.png?t=M276https://www.bilibili.com/video/BV17Z4y1w7qF?from=search&seid=4612262847067450332&spm_id_from=333.337.0.0

另外,我所用的数据: 我用的是Berlin52http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/icon-default.png?t=M276http://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左右

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Python实现遗传算法解决TSP旅行商问题的示例代码: ```python import random # 城市坐标 city_pos = [(60, 200), (180, 200), (80, 180), (140, 180), (20, 160), (100, 160), (200, 160), (140, 140), (40, 120), (100, 120), (180, 100), (60, 80), (120, 80), (180, 60), (20, 40), (100, 40), (200, 40), (20, 20), (60, 20), (160, 20)] # 种群大小 POP_SIZE = 500 # 城市数量 ITY_COUNT = len(city_pos) # 交叉概率 CROSS_RATE = 0.1 # 变异概率 MUTATION_RATE = 0.02 # 代数 N_GENERATIONS = 500 # 计算两个城市之间的距离 def distance(city1, city2): return ((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) ** 0.5 # 计算一条路径的总距离 def get_fitness(path): distance_sum = 0 for i in range(CITY_COUNT - 1): distance_sum += distance(city_pos[path[i]], city_pos[path[i+1]]) distance_sum += distance(city_pos[path[-1]], city_pos[path[0]]) return 1 / distance_sum # 初始化种群 def init_population(): population = [] for i in range(POP_SIZE): path = list(range(CITY_COUNT)) random.shuffle(path) population.append(path) return population # 选择 def select(population, fitness): idx = random.randint(0, POP_SIZE - 1) for i in range(POP_SIZE): if random.random() < fitness[i] / fitness.sum(): idx = i break return population[idx] # 交叉 def crossover(parent1, parent2): if random.random() < CROSS_RATE: child = [-1] * CITY_COUNT start = random.randint(0, CITY_COUNT - 1) end = random.randint(start, CITY_COUNT - 1) child[start:end+1] = parent1[start:end+1] for i in range(CITY_COUNT): if parent2[i] not in child: for j in range(CITY_COUNT): if child[j] == -1: child[j] = parent2[i] break return child else: return parent1 # 变异 def mutate(child): if random.random() < MUTATION_RATE: idx1, idx2 = random.sample(range(CITY_COUNT), 2) child[idx1], child[idx2] = child[idx2], child[idx1] return child # 遗传算法主函数 def genetic_algorithm(): population = init_population() for generation in range(N_GENERATIONS): fitness = [get_fitness(path) for path in population] best_path = population[fitness.index(max(fitness))] print("Generation:", generation, "| Best path length:", 1 / max(fitness)) new_population = [best_path] for i in range(POP_SIZE - 1): parent1 = select(population, fitness) parent2 = select(population, fitness) child = crossover(parent1, parent2) child = mutate(child) new_population.append(child) population = new_population return best_path # 运行遗传算法 best_path = genetic_algorithm() print("Best path:", best_path) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值