遗传算法(Genetic Algorithm, GA)是模拟生物在自然环境中的遗传和进化的过程而形成的自适应全局优化搜索算法。它借用了生物遗传学的观点,通过自然选择、遗传和变异等作用机制,实现各个个体适应性的提高。
生物遗传和进化的规律:
遗传学术语 | 遗传算法术语 |
---|---|
群体 | 可行解集 |
个体 | 可行解 |
染色体 | 可行解的编码 |
基因 | 可行解编码的分量 |
基因形式 | 遗传编码 |
应适度 | 评价函数值 |
选择 | 选择操作 |
交叉 | 交叉操作 |
变异 | 变异操作 |
二进制编码方法是遗传算法中最常用的一种编码方法,它使用的编码符号集是由二进制符号 0 0 0和 1 1 1所组成的二值符号集 { 0 , 1 } \{0,1\} {0,1},它所构成的个体基因型是一个二进制编码符号串。
假设某一参数的取值范围是 [ u m i n , u m a x ] [u_{\rm{min}},u_{\rm{max}}] [umin,umax],用长度为 L L L的二进制编码符号来表示该参数。则它总共能够产生 2 L 2^L 2L种不同的编码,参数编码时的对应关系如下:
000 ⋯ 000 = 0 u min 000 ⋯ 001 = 1 u min + δ ⋯ ⋯ 111 ⋯ 111 = 2 λ − 1 u max \begin{aligned} & 000\cdots 000 = 0 & u_{\min} \\ & 000\cdots 001 = 1 & u_{\min}+\delta \\ & \cdots & \cdots \\ & 111\cdots 111 = 2^\lambda-1 & u_{\max} \\ \end{aligned} 000⋯000=0000⋯001=1⋯111⋯111=2λ−1uminumin+δ⋯umax
二进制编码的精度为:
δ = u max − u min 2 λ − 1 \delta=\dfrac{u_{\max}-u_{\min}}{2^\lambda -1} δ=2λ−1umax−umin
假设某一个体的编码是: x : b λ b λ − 1 b λ − 2 ⋯ b 2 b 1 x:b_\lambda b_{\lambda-1} b_{\lambda-2} \cdots b_{2}b_{1} x:bλbλ−1bλ−2⋯b2b1,则对应的解码公式为:
x = u min + ( ∑ i = 1 λ b ⋅ 2 i − 1 ) δ x = u_{\min}+\left(\sum_{i=1}^{\lambda}{b\cdot 2^{i-1}}\right)\delta x=umin+(i=1∑λb⋅2i−1)δ
选择操作,是从旧的种群中选择适应度高的染色体,放入匹配集(缓冲区),为以后染色体交换、变异,产生新的染色体作准备。
适应度比例法,也叫作轮盘赌、转轮法,按各染色体适应度大小比例来决定其被选择数目的多少。第 i i i个染色体被选的概率为:
P c i = f ( x i ) ∑ j = 1 N p f ( x j ) P_{ci}=\dfrac{f\left(x_i\right)}{\sum_{j=1}^{N_p}{f\left(x_j\right)}} Pci=∑j=1Npf(xj)f(xi)
交叉操作,是指对两个相互配对的染色体依据交叉概率 P c P_c Pc按某种方式相互交换其部分基因,从而形成两个新的个体。
具体方法: 随机选择二个染色体(双亲染色体),随机指定一点或多点, ,进行交换,可得二个新的染色体(子辈染色体)。
双亲染色体如下, ∣ {\color{red}|} ∣为交换点,
A 11010 ∣ 110 B 01011 ∣ 011 \begin{aligned} A\quad & 11010{\color{red}|}110 \\ B\quad & 01011{\color{red}|}011 \\ \end{aligned} AB11010∣11001011∣011
则通过交叉操作,产生的新的子辈染色体为
A ′ 11010 0 1 1 B ′ 01011 1 1 0 \begin{aligned} A'\quad & 11010{\color{red}0\color{red}1\color{red}1} \\ B'\quad & 01011{\color{red}1\color{red}1\color{red}0} \\ \end{aligned} A′B′1101001101011110
变异操作,即基本位突变算子是指对个体编码串随机指定的某一位或某几位基因作突变运算。
对于基本遗传算法中用二进制编码符号串所表示的个体,若需要进行突变操作的某一基因座上的原有基因值为0,则突变操作将其变为1;反之,若原有基因值为1,则突变操作将其变为0 。
变异产生染色体的多样性,避免进化中早期成熟,陷入局部极值点,突变的概率很低。
父辈染色体如下, ∣ {\color{red}|} ∣为变异点,
A 1101 ∣ 011 ∣ 0 \begin{aligned} A\quad & 1101{\color{red}|}011{\color{red}|}0 \\ \end{aligned} A1101∣011∣0
则通过变异操作,产生的新的子辈染色体为
A ′ 1101 1 0 0 0 \begin{aligned} A'\quad & 1101{\color{red}1\color{red}0\color{red}0}0 \\ \end{aligned} A′11011000
种群规模 N p N_p Np:群体规模将影响遗传优化的最终结果以及遗传算法的执行效率。一般 N p N_p Np取10到200。
交叉概率 P c P_c Pc:交叉概率 P c P_c Pc控制着交叉操作被使用的频度。一般 P c P_c Pc取0.25到1.00。
变异概率 P m P_m Pm:变异在遗传算法中属于辅助性的搜索操作,它的目的是保持群体的多样性。一般 P m P_m Pm取0.001到0.1。
最大进化代数 G max G_{\text{max}} Gmax:最大进化代数 G max G_{\max} Gmax是表示遗传算法运行结束条件的一个参数,表示遗传算法运行到指定代数之后就停止运行。一般 G max G_{\max} Gmax取100到1000,视具体情况而定。
求 f ( x ) = x − 10 sin ( 5 x ) + 7 cos ( 4 x ) f(x)=x-10\sin(5x)+7\cos(4x) f(x)=x−10sin(5x)+7cos(4x)的最大值,其中 x x x的范围为 x ∈ [ 0 , 10 ] x\in[0,10] x∈[0,10]。
函数图像和结果:
迭代曲线:
% 遗传算法计算函数最大值
% f(x)=x - 10sin(5x) + 7cos(4x)
% 系统初始化
close all
clear
clc
% 定义相关变量
Np = 32; % 种群大小
L = 20; % 编码长度
Pc = 0.8; % 交叉概率
Pm = 0.1; % 变异概率
Gmax = 200; % 迭代次数
Xmin = 0; % 自变量范围
Xmax = 10; % 自变量范围
% 种群初始化
pop = randi([0,1],Np,L);
% 定义相关变量
x = zeros(Np,1); % 记录每一代的变量
trace = zeros(Gmax,1); % 记录每一代的结果
newPop = zeros(Np,L); % 记录新种群
% 开始迭代
for k = 1:Gmax
% 将二进制解码为定义域范围内的十进制
for i = 1:Np
U = pop(i,:);
m = 0;
for j = 1:L
m = U(j)*2^(j-1)+m;
end
x(i) = Xmin + m*(Xmax - Xmin)/(2^L-1);
end
Fit = f(x);
[maxFit,rr] = max(Fit);
minFit = min(Fit);
fBest = pop(rr,:);
xBest = x(rr);
Fit = (Fit - minFit)/(maxFit - minFit);
% 基于轮盘赌的选择操作
sum_Fit = sum(Fit);
fitvalue = Fit./sum_Fit;
fitvalue = cumsum(fitvalue);
ms = sort(rand(Np,1));
fiti = 1;
newi = 1;
while newi <= Np
if ms(newi)< fitvalue(fiti)
newPop(newi,:) = pop(fiti,:);
newi = newi + 1;
else
fiti = fiti + 1;
end
end
% 基于概率的交叉选择
for i = 1:2:Np
p = rand;
if p < Pc
q = randi(1,L);
for j = 1:L
if q(j) == 1
tmp = newPop(i+1,j);
newPop(i+1,j) = newPop(i,j);
newPop(i,j) = tmp;
end
end
end
end
% 基于概率的变异操作
i = 1;
while i <=round(Np*Pm)
h = randi([1,Np],1,1);
for j = 1:round(L*Pm)
g = randi([1,L],1,1);
newPop(h,g) =~ newPop(h,g);
end
i = i+1;
end
pop = newPop;
pop(1,:) = fBest;
trace(k) = maxFit;
end
xBest;
figure('name','迭代曲线','color','w')
plot(trace,'b','linewidth',1)
set(gca,'FontName','Times New Roman','FontSize',12);
xlabel('迭代次数','FontName','宋体');
ylabel('适应度值','FontName','宋体');
xlim([1,Gmax])
X = linspace(Xmin,Xmax,1000);
Y = f(X);
figure('name','函数图像','color','w')
plot(X,Y,'b','linewidth',1);
hold on
set(gca,'FontName','Times New Roman','FontSize',12);
xlabel('$x$','interpreter','Latex');
ylabel('$f(x)$','interpreter','Latex');
title('$f(x)=x - 10\sin(5x) + 7\cos(4x)$','interpreter','Latex');
ylim([-20,30])
[ymax,xindex] = max(Y);
line1 = plot(X(xindex),ymax,'go');
line2 = plot(xBest,f(xBest),'r*');
hold off
legend([line1,line2],{'Max','GA Result'},'Location','southeast')
function y = f(x)
y = x + 10*sin(5*x) + 7 * cos(4*x);
end
使用遗传算法求解旅行商问题,求解路程最短的路径。
求解结果:
迭代曲线:
% 遗传算法求解旅行商问题
% 系统初始化
clear
close all
clc
% 定义起点和终点
R1 = [50;50];
R2 = [500;660];
% 定义途经点
Points = [171.1867 431.8328 346.1714 823.4578 634.4461 381.5585 795.1999 200 620;
706.0461 276.9230 97.1318 694.8286 438.7444 765.5168 186.8726 500 860];
xy = [R1 Points R2];
N = size(xy,2);
% 计算距离矩阵
dmat = zeros(N,N); % 定义距离矩阵
for i = 2:N
for j = 1:i-1
d = norm(xy(:,i)-xy(:,j));
dmat(i,j) = d;
dmat(j,i) = d;
end
end
popSize = 32;
numIter = 300;
[N,~] = size(dmat);
n = N-2;
% 检测
popSize = 4*ceil(popSize/4);
numIter = max(1,round(real(numIter(1))));
% 初始化种群
pop = zeros(popSize,n);
pop(1,:) = (2:n+1);
for k = 2:popSize
pop(k,:) = randperm(n)+1;
end
% 遗传算法相关变量
globalMin = Inf; % 记录最小值
totalDist = zeros(1,popSize); % 记录总距离
distHistory = zeros(1,numIter); % 记录历史值
tmpPop = zeros(4,n); % 临时种群
newPop = zeros(popSize,n); % 新种群
% 遗传算法开始迭代
for iter = 1:numIter
% 评估种群中每一个个体
for p =1:popSize
d = dmat(1,pop(p,1));
for k = 2:n
d =d+dmat(pop(p,k-1),pop(p,k));
end
d = d + dmat(N,pop(p,n));
totalDist(p) = d;
end
% 找出最优路径
[minDist, index] = min(totalDist);
distHistory(iter) = minDist;
% 替代上一次进化汇总最好的染色体
globalMinOld = globalMin;
if(minDist < globalMin)
globalMin = minDist;
optRoute = pop(index,:);
end
% 遗传操作
randomOrder = randperm(popSize);
for p = 4:4:popSize
rtes = pop(randomOrder(p-3:p),:);
dists = totalDist(randomOrder(p-3:p));
[~,idx] = min(dists);
bestOf4Route = rtes(idx,:); % 每4个里面找一个最好的
routeInsertionPoints = sort(ceil(n*rand(1,2)));
I = routeInsertionPoints(1);
J = routeInsertionPoints(2);
% 由最好个体变异,得到其他3个个体
for k = 1:4
tmpPop(k,:) = bestOf4Route;
switch k
case 2 % 翻转
tmpPop(k,I:J) = tmpPop(k,J:-1:I);
case 3 % 交换
tmpPop(k,[I J]) = tmpPop(k,[J I]);
case 4 % 滑移
tmpPop(k,I:J) = tmpPop(k,[I+1:J I]);
otherwise
end
end
newPop(p-3:p,:) = tmpPop;
end
pop = newPop;
end
% 最优化路径
optRoute = [1 optRoute N]; % 补全起点和终点
% 绘图:地图
figure('name','地图','color',[1,1,1]);
plot(R1(1),R1(2),'ko');
text(R1(1)+20,R1(2)-20,'S','FontName','Times New Roman','FontSize',12);
hold on
axis equal
axis([0 1000 0 1000]);
plot(R2(1),R2(2),'k*');
text(R2(1)+20,R2(2)-20,'T','FontName','Times New Roman','FontSize',12);
plot(Points(1,:),Points(2,:),'k^');
set(gca,'FontName','Times New Roman','FontSize',12);
xlabel('$x/\rm{m}$','interpreter','Latex');
ylabel('$y/\rm{m}$','interpreter','Latex');
plot(xy(1,optRoute),xy(2,optRoute),'b','linewidth',1)
grid on
% 绘图:迭代曲线
xmax = length(distHistory);
figure('name','迭代曲线','color',[1,1,1]);
plot(distHistory,'b','linewidth',1);
xlim([1,xmax]);
set(gca,'FontName','Times New Roman','FontSize',12);
xlabel('迭代次数','FontName','宋体');
ylabel('$d/\rm{m}$','interpreter','Latex');
grid on
GA.py
GA.py
文件,用到的库matplotlib
、numpy
,以及一个数组相关操作的库Array.py
。
在使用时,注意修改导入
Array
时的路径!!!
"""
遗传算法
Author : Xu Zhe
Date : 2022-03-17
Brief : 遗传算法基础,遗传算法求解TSP
History : 2022-03-18 : 增加TSP部分
"""
import matplotlib.pyplot as plt
import numpy as np
from C_AlgorithmProgram.C1_DataStructure import Array as Arr
def genetic_algorithm(fun_fitness, x_min, x_max, n_pop, l_ch, p_c, p_m, g_max):
"""
基础遗传算法
:param fun_fitness: 适度值函数
:param x_min: 优化变量范围 最小值
:param x_max: 优化变量范围 最大值
:param n_pop: 种群规模
:param l_ch: 染色体长度
:param p_c: 交叉概率
:param p_m: 变异概率
:param g_max: 最大进化代数
:return: 最优变量, 最优适度值, 历史记录
"""
# 种群初始化:随机生成 pop_size 行 ch_length 列的 [0, 1] 矩阵
pop = np.random.randint(0, 2, (n_pop, l_ch))
# 记录每一代的所有个体对应的变量
x_list = np.zeros(n_pop)
# 记录每一代的所有个体对应的适应度
fitness_list = np.zeros(n_pop)
# 记录每一代的最优结果,绘制迭代曲线
history = np.zeros(g_max)
# 初始化变量
x_best = 0
fitness_max = 0
# 开始迭代
for ig in range(g_max):
# 适度值评估
for ip1 in range(n_pop):
# 取出染色体
chromosome = pop[ip1, :]
# 解码
x_list[ip1] = binary_decoding(chromosome, x_min, x_max)
# 计算种群中每个个体的适应度值
fitness_list[ip1] = fun_fitness(x_list[ip1])
# 适度值对大的个体及其索引
fitness_max = np.max(fitness_list)
idx = fitness_list.tolist().index(fitness_max)
# 更新最优个体和最优变量
ch_best = pop[idx, :]
x_best = x_list[idx]
history[ig] = fitness_max
# 对适度值列表进行归一化
fitness_min = np.min(fitness_list)
if fitness_min == fitness_max:
fitness_list = np.ones(n_pop)
else:
fitness_list = (fitness_list - fitness_min) / (fitness_max - fitness_min)
# 选择操作
pop = ga_selection(fitness_list, pop, n_pop)
# 交叉操作
pop = ga_crossover(pop, n_pop, l_ch, p_c)
# 变异操作
pop = ga_crossover(pop, n_pop, l_ch, p_m)
# 将最优个体放回种群中
pop[0, :] = ch_best.copy()
return x_best, fitness_max, history
def ga_selection(fitness_list, pop, n_pop):
"""
遗传算法选择算子
:param fitness_list: 适度值列表
:param pop: 种群
:param n_pop: 种群大小
:return: 新的种群
"""
# 复制一份新种群
pop_new = pop.copy()
# 对适度值列表求和
fitness_sum = np.sum(fitness_list)
# 归一化
fitness_norm = fitness_list / fitness_sum
# 逐项求和,计算概率
p_list = np.cumsum(fitness_norm)
# 按该率选择
for i in range(n_pop):
# 随机一个概率值
p = np.random.rand()
# 找到这个概率值对应的位置
idx = search_insert(p_list, p)
# 索引溢出检测
if idx >= n_pop:
idx = n_pop - 1
# 复制选择的个体,生成新的种群
pop_new[i, :] = (pop[idx, :]).copy()
return pop_new
def ga_crossover(pop, n_pop, l_ch, p_c):
"""
遗传算法交叉算子
:param pop: 种群
:param n_pop: 种群大小
:param l_ch: 染色体编码长度
:param p_c: 交叉概率
:return: 新的种群
"""
for i in range(0, n_pop, 2):
# 随机一个概率值
p = np.random.rand()
# 如果满足交叉概率,则进行交叉操作
if p <= p_c:
# 确定需要交叉的编码位置
idx = np.random.randint(0, 2, l_ch)
for j in range(l_ch):
# 如果是需要交叉的位置,则进行交换操作
if idx[j] == 1:
tmp = pop[i + 1, j]
pop[i + 1, j] = pop[i, j]
pop[i, j] = tmp
return pop
def ga_mutation(pop, n_pop, l_ch, p_m):
"""
遗传算法变异算子
:param pop: 种群
:param n_pop: 种群大小
:param l_ch: 染色体编码长度
:param p_m: 变异概率
:return: 新的种群
"""
for i in range(n_pop):
# 随机一个概率值
p = np.random.rand()
# 如果满足交叉概率,则进行变异操作
if p <= p_m:
# 确定需要变异的编码位置
idx = np.random.randint(0, 2, l_ch)
for j in range(l_ch):
# 如果是需要变异的位置,则进行翻转操作
if idx[j] == 1:
pop[i, j] = 1 - pop[i, j]
return pop
def search_insert(nums, target):
"""
二分查找法搜索插入位置
:param nums:数组
:param target:目标值
:return:插入位置或存在位置
"""
n = len(nums)
left = 0
right = n - 1
ans = n
while left <= right:
mid = ((right - left) >> 1) + left
if target <= nums[mid]:
ans = mid
right = mid - 1
else:
left = mid + 1
return ans
def binary_decoding(code, u_min, u_max):
"""
二进制解码
:param code: 二进制编码
:param u_min: 最小值
:param u_max: 最大值
:return: 对应的十进制数
"""
# 编码长度
lc = len(code)
# 编码精度
d = (u_max - u_min) / (2 ** lc - 1)
x = 0
# 二进制向十进制转化
for i in range(lc):
x += code[i] * (2 ** i)
# 按照精度和范围,求解对应的十进制数
return x * d + u_min
# 余胜威. MATLAB优化算法案例分析与应用[M]. 清华大学出版社, 2014:256-258.
# 第17章 基于GA的TSP求解
def genetic_algorithm_tsp(distance_mat, pop_size, g_max):
"""
遗传算法求解旅行商问题
:param distance_mat: 距离矩阵
:param pop_size: 种群大小
:param g_max: 最大迭代次数
:return: 序列,距离,历史记录
"""
# 计算点数
n_all = np.shape(distance_mat)[0]
n = n_all - 2
# 确保种群数时4的倍数,这与下面的遗传操作有关
pop_size = int(4 * np.ceil(pop_size / 4))
# 初始化种群
pop = np.zeros((pop_size, n), dtype=int)
pop[0, :] = np.arange(1, n + 1)
for ip3 in range(1, pop_size):
pop[ip3, :] = np.arange(1, n + 1)
np.random.shuffle(pop[ip3, :])
# 初始化相关变量
route_best = pop[0, :].copy() # 初始化最优路径
distance_min = global_min = np.inf # 初始化最短距离和全局最短
distance_list = np.zeros(pop_size) # 用于记录没一次迭代各条路径的距离
history = np.zeros(g_max) # 用于记录迭代曲线
pop_tmp = np.zeros((4, n)) # 用于记录临时4组个体
pop_new = np.zeros((pop_size, n))
# 开始迭代
for ig in range(g_max):
# 对每一个个体进行评估
for ip in range(pop_size):
d = distance_mat[0, int(pop[ip, 0])]
for k in range(1, n):
d = d + distance_mat[int(pop[ip, k - 1]), int(pop[ip, k])]
d = d + distance_mat[n_all - 1, int(pop[ip, n - 1])]
distance_list[ip] = d
# 找出最小值
distance_min, idx = Arr.get_min(distance_list)
history[ig] = distance_min
# 更新最优解
if distance_min < global_min:
global_min = distance_min
route_best = pop[idx, :].copy()
# 遗传操作
order = np.arange(pop_size)
np.random.shuffle(order) # 随机顺序
for ip2 in range(0, pop_size, 4):
# 选出四条路径
routes = pop[order[range(ip2, ip2 + 4)], :]
# 选出距离
distance_list_4 = distance_list[order[range(ip2, ip2 + 4)]]
tmp, idx = Arr.get_min(distance_list_4)
# 四个里面找出最好的
route_best_of_4 = routes[idx, :].copy()
# 设置变换点
insertion_points = np.sort(np.ceil(n * np.random.random((1, 2)))) - 1
left = int(insertion_points[0, 0])
right = int(insertion_points[0, 1])
for k in range(4):
pop_tmp[k, :] = route_best_of_4.copy()
if k == 1:
# 翻转操作
pop_tmp[k, :] = Arr.flip(pop_tmp[k, :], left, right)
if k == 2:
# 交换操作
pop_tmp[k, :] = Arr.swap(pop_tmp[k, :], left, right)
if k == 3:
# 滑移操作
pop_tmp[k, :] = Arr.slide(pop_tmp[k, :], left, right)
# 产生新的种群
pop_new[range(ip2, ip2 + 4), :] = pop_tmp.copy()
pop = pop_new.copy()
return route_best, distance_min, history
def test_function(x):
"""
优化算法测试函数 f(x) = x + 10 * sin(5 * x) + 7 * cos(4 * x)
:param x: 自变量
:return: 因变量
"""
return x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)
def ga_function_test():
x_min = 0
x_max = 10
pop_size = 32
ch_length = 20
pc = 0.8
pm = 0.1
g_max = 200
x_best, f_best, history = genetic_algorithm(test_function, x_min, x_max, pop_size, ch_length, pc, pm, g_max)
print(x_best)
print(f_best)
x = np.linspace(x_min, x_max, 500)
y = test_function(x)
y_max = np.max(y)
x_idx = y.tolist().index(y_max)
# plt.style.use('science')
plt.figure(dpi=120, figsize=(4, 2.5))
plt.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.18)
plt.rc('font', family='Times New Roman', size=10)
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
plt.plot(x, y, 'b')
plt.scatter(x[x_idx], y_max, marker='o', c='w', edgecolors='g', label="Max")
plt.plot(x_best, f_best, 'r*', label="GA Result")
plt.xlim((x_min, x_max))
plt.xlabel("X")
plt.ylabel("Y")
plt.legend(fancybox=False, edgecolor='k')
plt.grid(linestyle="--")
plt.figure(dpi=120, figsize=(4, 2.5))
plt.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.18)
plt.rc('font', family='Times New Roman', size=10)
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
plt.plot(history, 'b')
plt.xlim((0, g_max))
plt.xlabel("Index of Iteration")
plt.ylabel("Fitness")
plt.grid(linestyle="--")
plt.show()
def ga_tsp_test():
# 起始点
p_s = np.array([50, 50])
# 终点
p_t = np.array([950, 950])
# 途经点
p_w = np.array([[171.1867, 706.0461],
[431.8328, 206.9230],
[246.1714, 197.1318],
[823.4578, 694.8286],
[634.4461, 438.7444],
[381.5585, 765.5168],
[795.1999, 305.8726],
[255.4563, 514.8138],
[547.8462, 721.5890]])
# 合并成所有点
p_all = np.vstack((p_s, p_w, p_t))
# 计算所有点的数目
n_all = np.shape(p_all)[0]
# 距离矩阵
distance_mat = np.zeros((n_all, n_all))
for i in range(1, n_all):
for j in range(i):
d = np.linalg.norm(p_all[i, :] - p_all[j, :])
distance_mat[i, j] = d
distance_mat[j, i] = d
# 调用遗传算法求解
pop_size = 32
g_max = 300
route, distance, history = genetic_algorithm_tsp(distance_mat, pop_size, g_max)
print(distance)
# 处理路径
line = p_s
for i in range(n_all - 2):
line = np.vstack((line, p_all[int(route[i]), :]))
line = np.vstack((line, p_t))
# 绘图
fig = plt.figure(dpi=120, figsize=(4, 4))
fig.canvas.manager.set_window_title('计算结果')
plt.subplots_adjust(left=0.14, right=0.95, top=0.95, bottom=0.18)
plt.rc('font', family='Times New Roman', size=10)
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
plt.plot(p_s[0], p_s[1], 'g^', label="Start Point")
plt.plot(p_t[0], p_t[1], 'g*', label="Target Point")
plt.plot(p_w[:, 0], p_w[:, 1], 'bo', label="Way Points")
plt.plot(line[:, 0], line[:, 1], 'r-', label="Route")
plt.axis('equal')
plt.xlim((0, 1000))
plt.ylim((0, 1000))
plt.xlabel("X (m)")
plt.ylabel("Y (m)")
plt.grid(linestyle="--")
plt.legend(fancybox=False, edgecolor='k')
# 绘制迭代曲线
fig2 = plt.figure(dpi=120, figsize=(4, 2.5))
fig2.canvas.manager.set_window_title('迭代曲线')
plt.subplots_adjust(left=0.14, right=0.95, top=0.95, bottom=0.18)
plt.rc('font', family='Times New Roman', size=10)
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
plt.plot(history, 'b')
plt.xlim((0, g_max))
plt.xlabel("Index of Iteration")
plt.ylabel("Distance (m)")
plt.grid(linestyle="--")
# 显示绘图
plt.show()
if __name__ == '__main__':
ga_tsp_test()
Array.py
"""
数组
Author : Xu Zhe
Date : 2022-03-18
Brief : 数组基本操作
History : 2022-03-18 : 最大值、最小值、翻转、教化、滑移
"""
import numpy as np
def get_min(nums):
"""
数组最小值及其索引
:param nums:
:return:
"""
n = len(nums)
res = nums[0]
idx = 0
for i in range(n):
if nums[i] < res:
res = nums[i]
idx = i
return res, idx
def get_max(nums):
"""
数组最大值及其索引
:param nums:
:return:
"""
n = len(nums)
res = nums[0]
idx = 0
for i in range(n):
if nums[i] > res:
res = nums[i]
idx = i
return res, idx
def flip(nums, left, right):
"""
数组元素翻转
:param nums:
:param left: 翻转其实为止
:param right: 翻转结束为止
:return:
"""
arr = nums.copy()
if left >= right:
return arr
n = len(arr)
if right >= n:
return arr
tmp = arr[left: right+1].copy()
tmp = tmp[::-1]
arr[left: right + 1] = tmp.copy()
return arr
def swap(nums, left, right):
"""
数组元素交换
:param nums: 数组
:param left: 交换坐标1
:param right: 交换坐标2
:return:
"""
arr = nums.copy()
n = len(arr)
if right > n or left > n:
return arr
tmp = arr[left]
arr[left] = arr[right]
arr[right] = tmp
return arr
def slide(nums, left, right):
"""
数组元素滑移
:param nums: 数组
:param left: 滑移起始位置
:param right: 滑移结束位置
:return: 新数组
"""
arr = nums.copy()
if left >= right:
return arr
n = len(arr)
if right > n:
return arr
tmp = arr[left]
for i in range(left, right):
arr[i] = arr[i + 1]
arr[right] = tmp
return arr