建模算法基于遗传算法求解TSP问题(Python实现)
Posted 果州做题家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了建模算法基于遗传算法求解TSP问题(Python实现)相关的知识,希望对你有一定的参考价值。
【建模算法】基于遗传算法求解TSP问题(Python实现)
TSP (traveling salesman problem,旅行商问题)是典型的NP完全问题,即其最坏情况下的时间复杂度随着问题规模的增大按指数方式增长,到目前为止还未找到一个多项式时间的有效算法。本文探讨了基于遗传算法求解TSP问题的Python实现。
一、问题描述
本案例以31个城市为例,假定31个城市的位置坐标如表1所列。寻找出一条最短的遍历31个城市的路径。
城市编号 | X坐标 | Y坐标 | 城市编号 | X坐标 | Y坐标 |
---|---|---|---|---|---|
1 | 1.304 | 2.312 | 17 | 3.918 | 2.179 |
2 | 3.639 | 1.315 | 18 | 4.061 | 2.37 |
3 | 4.177 | 2.244 | 19 | 3.78 | 2.212 |
4 | 3.712 | 1.399 | 20 | 3.676 | 2.578 |
5 | 3.488 | 1.535 | 21 | 4.029 | 2.838 |
6 | 3.326 | 1.556 | 22 | 4.263 | 2.931 |
7 | 3.238 | 1.229 | 23 | 3.429 | 1.908 |
8 | 4.196 | 1.044 | 24 | 3.507 | 2.376 |
9 | 4.312 | 0.79 | 25 | 3.394 | 2.643 |
10 | 4.386 | 0.57 | 26 | 3.439 | 3.201 |
11 | 3.007 | 1.97 | 27 | 2.935 | 3.24 |
12 | 2.562 | 1.756 | 28 | 3.14 | 3.55 |
13 | 2.788 | 1.491 | 29 | 2.545 | 2.357 |
14 | 2.381 | 1.676 | 30 | 2.778 | 2.826 |
15 | 1.332 | 0.695 | 31 | 2.37 | 2.975 |
16 | 3.715 | 1.678 |
二、解题思路及步骤
1.遗传算法步骤:
第一步:初始化 t←0进化代数计数器;T是最大进化代数(也可以没有);随机生成M个个体作为初始群体P(t);
第二步:个体评价 计算P(t)中各个个体的适应度;
第三步:选择运算 将选择算子作用于群体;
第四步:交叉运算 将交叉算子作用于群体;
第五步:变异运算 将变异算子作用于群体,并通过以上运算得到下一代群体P(t + 1);
第六步:终止条件判断 t≦T:t← t+1 转到步骤2;t>T:终止 输出解。
2.遗传算法求解的一般过程:
1)确定决策变量及各种约束条件,即个体的表现型X和问题的解空间;
2)建立优化模型 (目标函数最大OR 最小) 数学描述形式 量化方法;
3)染色体编码方法;
4)解码方法;
5)个体适应度的量化评价方法 F(x)
6)设计遗传算子;
7)确定有关运行参数。
3.方法实现
编码方式
应用于TSP问题,选用整数编码,每个整数代表一个城市,一整条路径就是整个染色体编码;如此显式的编码,可以不用解码;
population = []
# 初始化种群
index = [i for i in range(w)]
for i in range(count):
x = index.copy()
random.shuffle(x)
gailiang(x)
population.append(x)
初始种群
随机生成初始种群,并计算这个初始种群的个体适应度。初始化种群时,采用改良版本,为了初始化一个较好的种群,如果随即交换两个城市的位置,如果总距离减小,那么就更新这个染色体。
# 初始种群的改良
def gailiang(x):
distance = get_total_distance(x)
gailiang_num = 0
while gailiang_num < gailiang_N:
while True:
a = random.randint(0, len(x) - 1)
b = random.randint(0, len(x) - 1)
if a != b:
break
new_x = x.copy()
temp_a = new_x[a]
new_x[a] = new_x[b]
new_x[b] = temp_a
if get_total_distance(new_x) < distance:
x = new_x.copy()
gailiang_num += 1
选择算子
选择总距离作为适应度函数,距离越小适应度越高,(存活率与总距离的倒数成正比)
# 适应度
def get_total_distance(x):
dista = 0
for i in range(len(x)):
if i == len(x) - 1:
dista += distance[x[i]][x[0]]
else:
dista += distance[x[i]][x[i + 1]]
return dista
交叉算子
1 部分映射交叉
选择交换部分,交换父代个体基因产生子代,然后建立映射表,根据映射表来消除基因冲突。
2 顺序交叉
在父代样本1中选择交换部分,根据父代1的交叉部分先生成子代1的部分基因片段,然后将父代2中未被选中的基因按顺序复制到子代1的空余部分;然后根据父代2选择交叉部分生成子代2,并将父代1中未选择的部分复制到子代2的空余;
3 基于位置的交叉
在父代1选择时随机选择需要交换的基因,根据交叉部分生成子代1;将父代2中未被选择到的基因复制到子代1中;然后根据父代2随机选择交叉部分生成子代2,并将父代1中未选择的部分复制到子代2的空余;
# 交叉繁殖
def crossover(parents):
target_count = count - len(parents)
children = []
while len(children) < target_count:
while True:
male_index = random.randint(0, len(parents)-1)
female_index = random.randint(0, len(parents)-1)
if male_index != female_index:
break
male = parents[male_index]
female = parents[female_index]
left = random.randint(0, len(male) - 2)
right = random.randint(left, len(male) - 1)
gen_male = male[left:right]
gen_female = female[left:right]
child_a = []
child_b = []
len_ca = 0
for g in male:
if len_ca == left:
child_a.extend(gen_female)
len_ca += len(gen_female)
if g not in gen_female:
child_a.append(g)
len_ca += 1
len_cb = 0
for g in female:
if len_cb == left:
child_b.extend(gen_male)
len_cb += len(gen_male)
if g not in gen_male:
child_b.append(g)
len_cb += 1
children.append(child_a)
children.append(child_b)
return children
变异算子
根据变异算子的概率,变异时随机选择两个不同的位置的基因进行交换。也可以采用三点变异法,随机生成abc三点,将ac基因片段与bc做交换。
# 变异操作
def mutation(children):
for i in range(len(children)):
if random.random() < mutation_rate:
while True:
u = random.randint(0, len(children[i]) - 1)
v = random.randint(0, len(children[i]) - 1)
if u != v:
break
temp_a = children[i][u]
children[i][u] = children[i][v]
children[i][v] = temp_a
更新种群
采用杰出父代+子代的方式来更新种群。
while i < iter_time:
# 自然选择
parents = nature_select(population)
# 繁殖
children = crossover(parents)
# 变异
mutation(children)
# 更新
population = parents + children
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)
i = i + 1
print(result_cur_best)
print(dist_cur_best)
我在求解时采用了在初始种群中进行改良的方法,收敛速度相对较快,求解结果比较满意。
三、求解结果
距离和城市序列:
TSP图和Loss图:
四、实现代码
#遗传算法求解TSP问题完整代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import math
import random
# 处理数据
coord = []
with open("data.txt", "r") as lines:
lines = lines.readlines()
for line in lines:
xy = line.split()
coord.append(xy)
coord = np.array(coord)
w, h = coord.shape
coordinates = np.zeros((w, h), float)
for i in range(w):
for j in range(h):
coordinates[i, j] = float(coord[i, j])
# print(coordinates)
# 得到距离矩阵
distance = np.zeros((w, w))
for i in range(w):
for j in range(w):
distance[i, j] = distance[j, i] = np.linalg.norm(coordinates[i] - coordinates[j])
# 种群数
count = 300
# 进化次数
iter_time = 1000
# 最优选择概率
retain_rate = 0.3 # 适应度前30%可以活下来
# 弱者生存概率
random_select_rate = 0.5
# 变异
mutation_rate = 0.1
# 改良
gailiang_N = 3000
# 适应度
def get_total_distance(x):
dista = 0
for i in range(len(x)):
if i == len(x) - 1:
dista += distance[x[i]][x[0]]
else:
dista += distance[x[i]][x[i + 1]]
return dista
# 初始种群的改良
def gailiang(x):
distance = get_total_distance(x)
gailiang_num = 0
while gailiang_num < gailiang_N:
while True:
a = random.randint(0, len(x) - 1)
b = random.randint(0, len(x) - 1)
if a != b:
break
new_x = x.copy()
temp_a = new_x[a]
new_x[a] = new_x[b]
new_x[b] = temp_a
if get_total_distance(new_x) < distance:
x = new_x.copy()
gailiang_num += 1
# 自然选择
def nature_select(population):
grad = [[x, get_total_distance(x)] for x in population]
grad = [x[0] for x in sorted(grad, key=lambda x: x[1])]
# 强者
retain_length = int(retain_rate * len(grad))
parents = grad[: retain_length]
# 生存下来的弱者
for ruozhe in grad[retain_length:]:
if random.random() < random_select_rate:
parents.append(ruozhe)
return parents
# 交叉繁殖
def crossover(parents):
target_count = count - len(parents)
children = []
while len(children) < target_count:
while True:
male_index = random.randint(0, len(parents)-1)
female_index = random.randint(0, len(parents)-1)
if male_index != female_index:
break
male = parents[male_index]
female = parents[female_index]
left = random.randint(0, len(male) - 2)
right = random.randint(left, len(male) - 1)
gen_male = male[left:right]
gen_female = female[left:right]
child_a = []
child_b = []
len_ca = 0
for g in male:
if len_ca == left:
child_a.extend(gen_female)
len_ca += len(gen_female)
if g not in gen_female:
child_a.append(g)
len_ca += 1
len_cb = 0
for g in female:
if len_cb == left:
child_b.extend(gen_male)
len_cb += len(gen_male)
if g not in gen_male:
child_b.append(g)
len_cb += 1
children.append(child_a)
children.append(child_b)
return children
# 变异操作
def mutation(children):
for i in range(len(children)):
if random.random() < mutation_rate:
while True:
u = random.randint(0, len(children[i]) - 1)
v = random.randint(0, len(children[i]) - 1)
if u != v:
break
temp_a = children[i][u]
children[i][u] = children[i][v]
children[i][v] = temp_a
def get_result(population):
grad = [[x, get_total_distance(x)] for x in population]
grad = sorted(grad, key=lambda x: x[1])
return grad[0][0], grad[0][1]
population = []
# 初始化种群
index = [i for i in range(w)]
for i in range(count):
x = index.copy()
random.shuffle(x)
gailiang(x)
population.append(x)
distance_list = []
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)
i = 0
while i < iter_time:
# 自然选择
parents = nature_select(population)
# 繁殖
children = crossover(parents)
# 变异
mutation(children)
# 更新
population = parents + children
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)
i = i + 1
print(result_cur_best)
print(dist_cur_best)
for i in range(len(result_cur_best)):
result_cur_best[i] += 1
result_path = result_cur_best
result_path.append(result_path[0])
print(result_path)
# 画图
X = []
Y = []
for index in result_path:
X.append(coordinates[index-1, 0])
Y.append(coordinates[index-1, 1])
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
plt.figure(1)
plt.plot(X, Y, '-o')
for i in range(len(X)):
plt.text(X[i] + 0.05, Y[i] + 0.05, str(result_path[i]), color='red')
plt.xlabel('横坐标')
plt.ylabel('纵坐标')
plt.title('轨迹图')
plt.figure(2)
plt.plot(np.array(distance_list))
plt.title('优化过程')
plt.ylabel('最优值')
plt.xlabel('代数(->)'.format(0, iter_time))
plt.show()
基于遗传算法实现TSP问题求解matlab代码
文章目录
一、理论基础
二、案例背景
1,问题描述
本案例以14个城市为例,假定14个城市的位置坐标如表1所列。寻找出一条最短的遍历14个城市的路径。
表1 14个城市的位置坐标
2,解决思路和步骤
(1).算法流程
遗传算法TSP问题的流程图如图1所示。
图1 遗传算法TSP问题求解的流程图
(2).遗传算法实现
三、MATLAB程序实现
(1).种群初始化
种群初始化函数InitPop的代码:
function Chrom = InitPop(NIND,N)
%% 初始化种群
% 输入:
% NIND:种群大小
% N: 个体染色体长度(这里为城市的个数)
% 输出:
% 初始种群
Chrom = zeros(NIND, N); % 用于存储种群
for i = 1:NIND
Chrom(i, :) = randperm(N); % 随机生成初始种群
end
(2).适应度函数
求种群个体的适应度函数Fitness的代码:
function FitnV = Fitness(len)
%% 适应度函数
% 输入:
% 个体的长度(TSP的距离)
% 输出:
% 个体的适应度值
FitnV = 1./len;
clear
clc
close all
load CityPosition1.mat
%% 初始化参数
NIND = 100; % 种群大小
MAXGEN = 200; % 最大遗传代数
Pc = 0.9; % 交叉概率
Pm = 0.05; % 变异概率
GGAP = 0.9; % 代沟(Generation gap)
D = Distance(X); % 生成距离矩阵
N = size(D, 1); % (14*14)
%% 初始化种群
Chrom = InitPop(NIND, N);
%% 在二维图上画出所有坐标点
% figure
% plot(X(:,1),X(:,2),'o');
%% 画出随机解的路线图
DrawPath(Chrom(1,:), X)
%% 输出随机解的路线和总距离
disp('初始种群中的一个随机值:')
OutputPath(Chrom(1,:));
Rlength = PathLength(D,Chrom(1,:));
disp(['总距离:', num2str(Rlength)]);
disp('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
%% 优化
gen = 0;
figure;
hold on; box on
xlim([0,MAXGEN])
title('优化过程')
xlabel('代数')
ylabel('最优值')
ObjV = PathLength(D,Chrom); %计算路线长度
preObjV = min(ObjV);
while gen < MAXGEN
%% 计算适应度
ObjV = PathLength(D,Chrom); % 计算路线长度
% fprintf('%d %1.10f\\n',gen,min(ObjV))
line([gen-1, gen], [preObjV, min(ObjV)]);
preObjV = min(ObjV);
FitnV = Fitness(ObjV);
%% 选择
SelCh = Select(Chrom,FitnV,GGAP);
%% 交叉操作
SelCh = Recombin(SelCh,Pc);
%% 变异
SelCh = Mutate(SelCh,Pm);
%% 进化逆转操作
SelCh = Reverse(SelCh,D);
%% 重插入子代的新种群
Chrom = Reins(Chrom,SelCh,ObjV);
%% 更新迭代次数
gen = gen+1 ;
end
%% 画出最优解的路线图
ObjV = PathLength(D,Chrom); %计算路线长度
[minObjV, minInd] = min(ObjV);
DrawPath(Chrom(minInd(1), :), X)
%% 输出最优解的路线和总距离
disp('最优解:')
p = OutputPath(Chrom(minInd(1), :));
disp(['总距离:', num2str(ObjV(minInd(1)))]);
disp('-------------------------------------------------------------')
结果分析
优化前的一个随机路线轨迹图如图2所示。
图2 随机路线图
初始种群中的一个随机值:
10—>4—>8—>11—>9—>13—>3—>12—>14—>6—>1—>5—>2—>7—>10
总距离:70.3719
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
优化后的路线图如图3所示。
图3 最优解路线图
最优解:
5—>4—>3—>14—>2—>1—>10—>9—>11—>8—>13—>7—>12—>6—>5
总距离:29.3405
-------------------------------------------------------------
优化迭代图如图4所示。
图4 遗传算法进化过程图
由优化图可以看出,优化前后路径长度得到很大改进,接近40代以后路径长度已经保持不变了,可以认为是最优解了,总距离由优化前的70.3719变为29.3405,减为原来的41.7%。
具体细节可参考:
链接:https://pan.baidu.com/s/16vnho_Uf6O1JdoVx9L2enQ
提取码:shgr
四、遗传算法的改进
上述程序中,对遗传算法做了以下两处改进。
1. 使用精英策略
子代种群中的最优个体永远不会比父代最优的个体差,这样使得父代的好的个体不至于由于交叉或者变异操作而丢失。
2. 使用进化逆转操作
同交叉算子相比较,逆转算子能使子代继承亲代的较多信息。
五、算法的局限性
当问题规模n nn比较小时,得到的一般都是最优解;当规模比较大时,一般只能得到近似解。这时可以通过增大种群大小和增加最大遗传代数使得优化值更接近最优解。
六、参考文献
[1] 储理才. 基于MATLAB的遗传算法程序设计及TSP问题求解[J]. 集美大学学报:自然科学版, 2001.
[2] 郁磊等. MATLAB智能算法30个案例分析(第2版)[M].北京航空航天大学出版社.2015年.
以上是关于建模算法基于遗传算法求解TSP问题(Python实现)的主要内容,如果未能解决你的问题,请参考以下文章
TSP基于matlab遗传算法求解旅行商问题含Matlab源码 1337期
TSP基于matlab遗传算法求解13城市旅行商问题含Matlab源码 1255期
TSP基于matlab GUI遗传算法求解旅行商问题含Matlab源码 1333期