yhzhu99/mtsp: 群体智能大作业:基于仿生群智算法的无人机任务分配 (多旅行商问题的求解) (github.com)
一、思路
预先设置无人机数目,途径点,然后迭代计算总的最短路径,最后绘制结果图
二、代码
plot_util.py
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签SimHei
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# from matplotlib import colors as mcolors
# matplotlib_colors = list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())
matplotlib_colors = [
"black",
"red",
"yellow",
"grey",
"brown",
"darkred",
"peru",
"darkorange",
"darkkhaki",
"steelblue",
"blue",
"cyan",
"green",
"navajowhite",
"lightgrey",
"lightcoral",
"mediumblue",
"midnightblue",
"blueviolet",
"violet",
"fuchsia",
"mediumvioletred",
"hotpink",
"crimson",
"lightpink",
"slategray",
"lime",
"springgreen",
"teal",
"beige",
"olive",
]
def find_indices(list_to_check, item_to_find):
indices = []
for idx, value in enumerate(list_to_check):
if np.array_equal(value, item_to_find):
indices.append(idx)
return indices
def plot_results(Best_path, iterations, best_record):
# print(find_indices(Best_path, [0, 0]))
# Best_path = np.vstack([Best_path, Best_path[0]])
# Best_path = np.vstack([Best_path[0], Best_path])
# print(Best_path[0], Best_path[-1])
if not np.array_equal(Best_path[0], [0, 0]):
Best_path = np.vstack([[0, 0], Best_path])
if not np.array_equal(Best_path[-1], [0, 0]):
Best_path = np.vstack([Best_path, [0, 0]])
#print(Best_path)
found_start_points_indices = find_indices(Best_path, [0, 0])
result_paths = []
for j in range(len(found_start_points_indices) - 1):
from_index = found_start_points_indices[j]
end_index = found_start_points_indices[j + 1]
path = []
for k in range(from_index, end_index + 1):
path.append(Best_path[k])
path = np.array(path)
result_paths.append(path)
# print(Best_path)
#print(result_paths)
fig, axs = plt.subplots(1, 2, sharex=False, sharey=False)
axs[0].scatter(Best_path[:, 0], Best_path[:, 1],c='r') #绘制点
for ix, path in enumerate(result_paths): #result_paths包含不同无人机的路线
axs[0].plot(path[:, 0], path[:, 1], color=matplotlib_colors[ix], alpha=1.0)
# axs[0].plot(Best_path[:, 0], Best_path[:, 1], color="green", alpha=0.1)
# Draw start point 绘制起点
axs[0].plot([0], [0], marker="*", markersize=20, color="red")
axs[0].set_title("最佳搜索结果")
axs[1].plot(iterations, best_record)
axs[1].set_title("Convergence Curve 收敛曲线")
plt.show()
aco.py
import math
import matplotlib.pyplot as plt
import numpy as np
import plot_util
#蚁群算法
class ACO(object):
def __init__(self, num_city, data, num_drones):
self.m = 50 # 蚂蚁数量
self.alpha = 1 # 信息素重要程度因子
self.beta = 5 # 启发函数重要因子
self.rho = 0.1 # 信息素挥发因子
self.Q = 1 # 常量系数
self.num_city = num_city # 城市规模
self.num_drones = num_drones # 有多少架无人机参与任务
self.location = data # 城市坐标
self.Tau = np.zeros([num_city, num_city]) # 信息素矩阵
self.Table = [[0 for _ in range(num_city)] for _ in range(self.m)] # 生成的蚁群
self.iter = 1
self.dis_mat = self.compute_dis_mat(num_city, self.location) # 计算城市之间的距离矩阵
self.Eta = 10.0 / self.dis_mat # 启发式函数
self.paths = None # 蚁群中每个个体的长度
# 存储存储每个温度下的最终路径,画出收敛图
self.iter_x = []
self.iter_y = []
# self.start_points_idx = []
# self.greedy_init(self.dis_mat,100,num_city)
def greedy_init(self, dis_mat, num_total, num_city):
start_index = 0
result = []
for i in range(num_total):
rest = [x for x in range(0, num_city)]
# 所有起始点都已经生成了
if start_index >= num_city:
start_index = np.random.randint(0, num_city)
result.append(result[start_index].copy())
continue
current = start_index
rest.remove(current)
# 找到一条最近邻路径
result_one = [current]
while len(rest) != 0:
tmp_min = math.inf
tmp_choose = -1
for x in rest:
if dis_mat[current][x] < tmp_min:
tmp_min = dis_mat[current][x]
tmp_choose = x
current = tmp_choose
result_one.append(tmp_choose)
rest.remove(tmp_choose)
result.append(result_one)
start_index += 1
pathlens = self.compute_paths(result)
sortindex = np.argsort(pathlens)
index = sortindex[0]
result = result[index]
for i in range(len(result) - 1):
s = result[i]
s2 = result[i + 1]
self.Tau[s][s2] = 1
self.Tau[result[-1]][result[0]] = 1
# for i in range(num_city):
# for j in range(num_city):
# return result
# 轮盘赌选择
def rand_choose(self, p):
x = np.random.rand()
for i, t in enumerate(p):
x -= t
if x <= 0:
break
return i
# 生成蚁群
def get_ants(self, num_city):
for i in range(self.m):
start = np.random.randint(num_city - 1)
self.Table[i][0] = start
unvisit = list([x for x in range(num_city) if x != start])
current = start
j = 1
while len(unvisit) != 0:
P = []
# 通过信息素计算城市之间的转移概率
for v in unvisit:
P.append(
self.Tau[current][v] ** self.alpha
* self.Eta[current][v] ** self.beta
)
P_sum = sum(P)
P = [x / P_sum for x in P]
# 轮盘赌选择一个一个城市
index = self.rand_choose(P)
current = unvisit[index]
self.Table[i][j] = current
unvisit.remove(current)
j += 1
# 关键: 修改distance matrix,适配多旅行商问题
# 计算不同城市之间的距离
def compute_dis_mat(self, num_city, location):
# print("Location:", location)
dis_mat = np.zeros((num_city, num_city))
for i in range(num_city):
for j in range(num_city):
if i == j:
dis_mat[i][j] = np.inf
continue
a = location[i]
b = location[j]
tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)]))
dis_mat[i][j] = tmp
# self.start_points_idx = to_process_idx
# print("to process indices:", to_process_idx)
for i in to_process_idx:
for j in to_process_idx:
# print("processing:", i, j, dis_mat[i][j])
dis_mat[i][j] = np.inf
return dis_mat
# 计算一条路径的长度
def compute_pathlen(self, tmp_path, dis_mat):
# print("Start!!!")
path = tmp_path.copy()
if path[0] not in to_process_idx:
path.insert(0, 0)
if path[-1] not in to_process_idx:
path.append(0)
a = path[0]
b = path[-1]
result = dis_mat[a][b] # 首末城市之间的距离
if a in to_process_idx and b in to_process_idx:
result = 0
for i in range(len(path) - 1):
a = path[i]
b = path[i + 1]
if a in to_process_idx and b in to_process_idx:
result += 0
else:
result += dis_mat[a][b]
# 关键:此时,原点-原点的距离不再是inf,而是0
# print("End!!!")
return result
# 计算一个群体的长度
def compute_paths(self, paths):
result = []
for one in paths:
length = self.compute_pathlen(one, self.dis_mat)
result.append(length)
return result
# 更新信息素
def update_Tau(self):
delta_tau = np.zeros([self.num_city, self.num_city])
paths = self.compute_paths(self.Table)
for i in range(self.m):
for j in range(self.num_city - 1):
a = self.Table[i][j]
b = self.Table[i][j + 1]
delta_tau[a][b] = delta_tau[a][b] + self.Q / paths[i]
a = self.Table[i][0]
b = self.Table[i][-1]
delta_tau[a][b] = delta_tau[a][b] + self.Q / paths[i]
self.Tau = (1 - self.rho) * self.Tau + delta_tau
def aco(self):
best_lenth = math.inf
best_path = None
early_stop_cnt = 0
for cnt in range(epochs):
# 生成新的蚁群
self.get_ants(self.num_city) # out>>self.Table
self.paths = self.compute_paths(self.Table)
# 取该蚁群的最优解
tmp_lenth = min(self.paths)
tmp_path = self.Table[self.paths.index(tmp_lenth)]
# 可视化初始的路径
if cnt == 0:
init_show = self.location[tmp_path]
init_show = np.vstack([init_show, init_show[0]])
# 更新最优解
if tmp_lenth < best_lenth:
best_lenth = tmp_lenth
best_path = tmp_path
early_stop_cnt = 0
else:
early_stop_cnt += 1
if early_stop_cnt == 20: # 若连续20次没有性能提升,则早停
break
# 更新信息素
self.update_Tau()
# 保存结果
self.iter_x.append(cnt)
self.iter_y.append(best_lenth)
print(f"Epoch {cnt:3} 最短路径:{best_lenth:.3f}")
#print(best_path)
return best_lenth, best_path
def run(self):
best_length, best_path = self.aco()
return self.location[best_path], best_length
seed = 42
num_drones = 30 #无人机数目
num_city = 30 #需要遍历的目标点数目
epochs = 100 #迭代次数
# 固定随机数
np.random.seed(seed)
## 初始化坐标 (第一个点是基地的起点,起点的坐标是 0,0 )
data = [[0, 0]] #点集合,城市位置
for i in range(num_city - 1):
while True:
x = np.random.randint(-250, 250)#随机生成途径点的x,y
y = np.random.randint(-250, 250)
if x != 0 or y != 0:
break
data.append([x, y])
print("途径点坐标:", data[i])
data = np.array(data)
# print(data, data.shape)
# 关键:有N架无人机,则再增加N-1个`点` (坐标是起始点),这些点之间的距离是inf
for d in range(num_drones - 1):
data = np.vstack([data, data[0]]) #按垂直方向(行顺序)堆叠数组构成一个新的数组
num_city += 1 # 增加欺骗城市
to_process_idx = [0]
# print("start point:", location[0])
for d in range(1, num_drones): # 1, ... drone-1
# print("added base point:", location[num_city - d])
to_process_idx.append(num_city - d)
# print(data)
# print("City len assert:", num_city, data.shape[0])
# print(show_data, show_data.shape)
aco = ACO(num_city=data.shape[0], data=data.copy(), num_drones=num_drones) #城市位置 无人机数
Best_path, Best = aco.run()
iterations = aco.iter_x #迭代次数
best_record = aco.iter_y #每次迭代对应的最短路径,用来绘制收敛曲线
print(f"Best Path Length: {Best:.3f} Best_path:{Best_path} ") #\n iter_x:{iterations} \n iter_y:{best_record}")
plot_util.plot_results(Best_path, iterations, best_record)#
pso.py
import math
import random
import matplotlib.pyplot as plt
import numpy as np
import plot_util
class PSO(object):
def __init__(self, num_city, data):
self.num = 200 # 粒子数目
self.num_city = num_city # 城市数
self.location = data # 城市的位置坐标
# 计算距离矩阵
self.dis_mat = self.compute_dis_mat(num_city, self.location) # 计算城市之间的距离矩阵
# 初始化所有粒子
self.particals = self.random_init(self.num, num_city)
# self.particals = self.greedy_init(
# self.dis_mat, num_total=self.num, num_city=num_city
# )
self.lenths = self.compute_paths(self.particals)
# 得到初始化群体的最优解
init_l = min(self.lenths)
init_index = self.lenths.index(init_l)
init_path = self.particals[init_index]
# 画出初始的路径图
init_show = self.location[init_path]
# 记录每个个体的当前最优解
self.local_best = self.particals
self.local_best_len = self.lenths
# 记录当前的全局最优解,长度是iteration
self.global_best = init_path
self.global_best_len = init_l
# 输出解
self.best_l = self.global_best_len
self.best_path = self.global_best
# 存储每次迭代的结果,画出收敛图
self.iter_x = [0]
self.iter_y = [init_l]
def greedy_init(self, dis_mat, num_total, num_city):
start_index = 0
result = []
for i in range(num_total):
rest = [x for x in range(0, num_city)]
# 所有起始点都已经生成了
if start_index >= num_city:
start_index = np.random.randint(0, num_city)
result.append(result[start_index].copy())
continue
current = start_index
rest.remove(current)
# 找到一条最近邻路径
result_one = [current]
while len(rest) != 0:
tmp_min = math.inf
tmp_choose = -1
for x in rest:
# print("---", current, x, dis_mat[current][x])
if dis_mat[current][x] < tmp_min:
tmp_min = dis_mat[current][x]
tmp_choose = x
if tmp_choose == -1: # 此种情况仅可能发生在剩的都是基地点
tmp_choose = rest[0]
# print("tmp_choose:", tmp_choose)
current = tmp_choose
result_one.append(tmp_choose)
# print(current, rest)
rest.remove(tmp_choose)
# print(rest)
result.append(result_one)
start_index += 1
# print(len(result), len(result[0]))
return result
# 随机初始化
def random_init(self, num_total, num_city):
tmp = [x for x in range(num_city)]
result = []
for i in range(num_total):
random.shuffle(tmp)
result.append(tmp.copy())
return result
# 计算不同城市之间的距离
def compute_dis_mat(self, num_city, location):
dis_mat = np.zeros((num_city, num_city))
for i in range(num_city):
for j in range(num_city):
if i == j:
dis_mat[i][j] = np.inf
continue
a = location[i]
b = location[j]
tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)]))
dis_mat[i][j] = tmp
for i in to_process_idx:
for j in to_process_idx:
# print("processing:", i, j, dis_mat[i][j])
dis_mat[i][j] = np.inf
return dis_mat
# 计算路径长度
def compute_pathlen(self, tmp_path, dis_mat):
path = tmp_path.copy()
if path[0] not in to_process_idx:
path.insert(0, 0)
if path[-1] not in to_process_idx:
path.append(0)
try:
a = path[0]
b = path[-1]
except:
import pdb
pdb.set_trace()
result = dis_mat[a][b] # 首末城市之间的距离
if a in to_process_idx and b in to_process_idx:
result = 0
for i in range(len(path) - 1):
a = path[i]
b = path[i + 1]
if a in to_process_idx and b in to_process_idx:
result += 0
else:
result += dis_mat[a][b]
return result
# 计算一个群体的长度
def compute_paths(self, paths):
result = []
for one in paths:
length = self.compute_pathlen(one, self.dis_mat)
result.append(length)
return result
# 评估当前的群体
def eval_particals(self):
min_lenth = min(self.lenths)
min_index = self.lenths.index(min_lenth)
cur_path = self.particals[min_index]
# 更新当前的全局最优
if min_lenth < self.global_best_len:
self.global_best_len = min_lenth
self.global_best = cur_path
# 更新当前的个体最优
for i, l in enumerate(self.lenths):
if l < self.local_best_len[i]:
self.local_best_len[i] = l
self.local_best[i] = self.particals[i]
# 粒子交叉
def cross(self, cur, best):
one = cur.copy()
l = [t for t in range(self.num_city)]
t = np.random.choice(l, 2)
x = min(t)
y = max(t)
cross_part = best[x:y]
tmp = []
for t in one:
if t in cross_part:
continue
tmp.append(t)
# 两种交叉方法
one = tmp + cross_part
l1 = self.compute_pathlen(one, self.dis_mat)
one2 = cross_part + tmp
l2 = self.compute_pathlen(one2, self.dis_mat)
if l1 < l2:
return one, l1
else:
return one, l2
# 粒子变异
def mutate(self, one):
one = one.copy()
l = [t for t in range(self.num_city)]
t = np.random.choice(l, 2)
x, y = min(t), max(t)
one[x], one[y] = one[y], one[x]
l2 = self.compute_pathlen(one, self.dis_mat)
return one, l2
# 迭代操作
def pso(self):
early_stop_cnt = 0
for cnt in range(epochs):
# 更新粒子群
for i, one in enumerate(self.particals):
tmp_l = self.lenths[i]
# 与当前个体局部最优解进行交叉
new_one, new_l = self.cross(one, self.local_best[i])
if new_l < self.best_l:
self.best_l = tmp_l
self.best_path = one
if new_l < tmp_l or np.random.rand() < 0.1:
one = new_one
tmp_l = new_l
# 与当前全局最优解进行交叉
new_one, new_l = self.cross(one, self.global_best)
if new_l < self.best_l:
self.best_l = tmp_l
self.best_path = one
if new_l < tmp_l or np.random.rand() < 0.1:
one = new_one
tmp_l = new_l
# 变异
one, tmp_l = self.mutate(one)
if new_l < self.best_l:
self.best_l = tmp_l
self.best_path = one
if new_l < tmp_l or np.random.rand() < 0.1:
one = new_one
tmp_l = new_l
# 更新该粒子
self.particals[i] = one
self.lenths[i] = tmp_l
# 评估粒子群,更新个体局部最优和个体当前全局最优
self.eval_particals()
# 更新输出解
if self.global_best_len < self.best_l:
self.best_l = self.global_best_len
self.best_path = self.global_best
early_stop_cnt = 0
else:
early_stop_cnt += 1
if early_stop_cnt == 50: # 若连续50次没有性能提升,则早停
break
print(f"Epoch {cnt:3}: {self.best_l:.3f}")
self.iter_x.append(cnt)
self.iter_y.append(self.best_l)
return self.best_l, self.best_path
def run(self):
best_length, best_path = self.pso()
# 画出最终路径
return self.location[best_path], best_length
seed = 42
num_drones = 20
num_city = 100
epochs = 50
# 固定随机数
np.random.seed(seed)
random.seed(seed)
## 初始化坐标 (第一个点是基地的起点,起点的坐标是 0,0 )
data = [[0, 0]]
for i in range(num_city - 1):
while True:
x = np.random.randint(-250, 250)
y = np.random.randint(-250, 250)
if x != 0 or y != 0:
break
data.append([x, y])
data = np.array(data)
# 关键:有N架无人机,则再增加N-1个`点` (坐标是起始点),这些点之间的距离是inf
for d in range(num_drones - 1):
data = np.vstack([data, data[0]])
num_city += 1 # 增加欺骗城市
to_process_idx = [0]
for d in range(1, num_drones): # 1, ... drone-1
to_process_idx.append(num_city - d)
model = PSO(num_city=data.shape[0], data=data.copy())
Best_path, Best = model.run()
iterations = model.iter_x
best_record = model.iter_y
print(f"Best Path Length: {Best:.3f}")
plot_util.plot_results(Best_path, iterations, best_record)
ga.py
import math
import random
import matplotlib.pyplot as plt
import numpy as np
import plot_util
class GA(object):
def __init__(self, num_city, num_total, data):
self.num_city = num_city
self.num_total = num_total
self.scores = []
# self.iteration = iteration
self.location = data
self.ga_choose_ratio = 0.2
self.mutate_ratio = 0.05
# fruits中存每一个个体是下标的list
self.dis_mat = self.compute_dis_mat(num_city, data)
# self.fruits = self.greedy_init(self.dis_mat, num_total, num_city)
self.fruits = self.random_init(num_total, num_city)
# 显示初始化后的最佳路径
scores = self.compute_adp(self.fruits)
sort_index = np.argsort(-scores)
init_best = self.fruits[sort_index[0]]
init_best = self.location[init_best]
# 存储每个iteration的结果,画出收敛图
self.iter_x = [0]
self.iter_y = [1.0 / scores[sort_index[0]]]
def random_init(self, num_total, num_city):
tmp = [x for x in range(num_city)]
result = []
for i in range(num_total):
random.shuffle(tmp)
result.append(tmp.copy())
# print("Lens:", len(result), len(result[0]))
return result
def greedy_init(self, dis_mat, num_total, num_city):
start_index = 0
result = []
for i in range(num_total):
rest = [x for x in range(0, num_city)]
# 所有起始点都已经生成了
if start_index >= num_city:
start_index = np.random.randint(0, num_city)
result.append(result[start_index].copy())
continue
current = start_index
rest.remove(current)
# 找到一条最近邻路径
result_one = [current]
while len(rest) != 0:
tmp_min = math.inf
tmp_choose = -1
for x in rest:
# print("---", current, x, dis_mat[current][x])
if dis_mat[current][x] < tmp_min:
tmp_min = dis_mat[current][x]
tmp_choose = x
if tmp_choose == -1: # 此种情况仅可能发生在剩的都是基地点
tmp_choose = rest[0]
# print("tmp_choose:", tmp_choose)
current = tmp_choose
result_one.append(tmp_choose)
# print(current, rest)
rest.remove(tmp_choose)
# print(rest)
result.append(result_one)
start_index += 1
# print(len(result), len(result[0]))
return result
# 计算不同城市之间的距离
def compute_dis_mat(self, num_city, location):
dis_mat = np.zeros((num_city, num_city))
for i in range(num_city):
for j in range(num_city):
if i == j:
dis_mat[i][j] = np.inf
continue
a = location[i]
b = location[j]
tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)]))
dis_mat[i][j] = tmp
for i in to_process_idx:
for j in to_process_idx:
# print("processing:", i, j, dis_mat[i][j])
dis_mat[i][j] = np.inf
return dis_mat
# 计算路径长度
def compute_pathlen(self, tmp_path, dis_mat):
path = tmp_path.copy()
if path[0] not in to_process_idx:
path.insert(0, 0)
if path[-1] not in to_process_idx:
path.append(0)
try:
a = path[0]
b = path[-1]
except:
import pdb
pdb.set_trace()
result = dis_mat[a][b] # 首末城市之间的距离
if a in to_process_idx and b in to_process_idx:
result = 0
for i in range(len(path) - 1):
a = path[i]
b = path[i + 1]
if a in to_process_idx and b in to_process_idx:
result += 0
else:
result += dis_mat[a][b]
return result
# 计算种群适应度
def compute_adp(self, fruits):
adp = []
for fruit in fruits:
if isinstance(fruit, int):
import pdb
pdb.set_trace()
length = self.compute_pathlen(fruit, self.dis_mat)
adp.append(1.0 / length)
return np.array(adp)
def swap_part(self, list1, list2):
index = len(list1)
list = list1 + list2
list = list[::-1]
return list[:index], list[index:]
def ga_cross(self, x, y):
len_ = len(x)
assert len(x) == len(y)
path_list = [t for t in range(len_)]
order = list(random.sample(path_list, 2))
order.sort()
start, end = order
# 找到冲突点并存下他们的下标,x中存储的是y中的下标,y中存储x与它冲突的下标
tmp = x[start:end]
x_conflict_index = []
for sub in tmp:
index = y.index(sub)
if not (index >= start and index < end):
x_conflict_index.append(index)
y_confict_index = []
tmp = y[start:end]
for sub in tmp:
index = x.index(sub)
if not (index >= start and index < end):
y_confict_index.append(index)
assert len(x_conflict_index) == len(y_confict_index)
# 交叉
tmp = x[start:end].copy()
x[start:end] = y[start:end]
y[start:end] = tmp
# 解决冲突
for index in range(len(x_conflict_index)):
i = x_conflict_index[index]
j = y_confict_index[index]
y[i], x[j] = x[j], y[i]
assert len(set(x)) == len_ and len(set(y)) == len_
return list(x), list(y)
def ga_parent(self, scores, ga_choose_ratio):
sort_index = np.argsort(-scores).copy()
sort_index = sort_index[0 : int(ga_choose_ratio * len(sort_index))]
parents = []
parents_score = []
for index in sort_index:
parents.append(self.fruits[index])
parents_score.append(scores[index])
return parents, parents_score
def ga_choose(self, genes_score, genes_choose):
sum_score = sum(genes_score)
score_ratio = [sub * 1.0 / sum_score for sub in genes_score]
rand1 = np.random.rand()
rand2 = np.random.rand()
index1, index2 = 0, 0
for i, sub in enumerate(score_ratio):
if rand1 >= 0:
rand1 -= sub
if rand1 < 0:
index1 = i
if rand2 >= 0:
rand2 -= sub
if rand2 < 0:
index2 = i
if rand1 < 0 and rand2 < 0:
break
return list(genes_choose[index1]), list(genes_choose[index2])
def ga_mutate(self, gene):
path_list = [t for t in range(len(gene))]
order = list(random.sample(path_list, 2))
start, end = min(order), max(order)
tmp = gene[start:end]
# np.random.shuffle(tmp)
tmp = tmp[::-1]
gene[start:end] = tmp
return list(gene)
def ga(self):
# 获得优质父代
scores = self.compute_adp(self.fruits)
# 选择部分优秀个体作为父代候选集合
parents, parents_score = self.ga_parent(scores, self.ga_choose_ratio)
tmp_best_one = parents[0]
tmp_best_score = parents_score[0]
# 新的种群fruits
fruits = parents.copy()
# 生成新的种群
while len(fruits) < self.num_total:
# 轮盘赌方式对父代进行选择
gene_x, gene_y = self.ga_choose(parents_score, parents)
# 交叉
gene_x_new, gene_y_new = self.ga_cross(gene_x, gene_y)
# 变异
if np.random.rand() < self.mutate_ratio:
gene_x_new = self.ga_mutate(gene_x_new)
if np.random.rand() < self.mutate_ratio:
gene_y_new = self.ga_mutate(gene_y_new)
x_adp = 1.0 / self.compute_pathlen(gene_x_new, self.dis_mat)
y_adp = 1.0 / self.compute_pathlen(gene_y_new, self.dis_mat)
# 将适应度高的放入种群中
if x_adp > y_adp and (not gene_x_new in fruits):
fruits.append(gene_x_new)
elif x_adp <= y_adp and (not gene_y_new in fruits):
fruits.append(gene_y_new)
self.fruits = fruits
return tmp_best_one, tmp_best_score
def run(self):
BEST_LIST = None
best_score = -math.inf
self.best_record = []
early_stop_cnt = 0
for i in range(epochs):
tmp_best_one, tmp_best_score = self.ga()
self.iter_x.append(i)
self.iter_y.append(1.0 / tmp_best_score)
if tmp_best_score > best_score:
best_score = tmp_best_score
BEST_LIST = tmp_best_one
early_stop_cnt = 0
else:
early_stop_cnt += 1
if early_stop_cnt == 50: # 若连续50次没有性能提升,则早停
break
self.best_record.append(1.0 / best_score)
best_length = 1.0 / best_score
print(f"Epoch {i:3}: {best_length:.3f}")
# print(1.0 / best_score)
return self.location[BEST_LIST], 1.0 / best_score
seed = 42
num_drones = 30
num_city = 50
epochs = 30
# 固定随机数
np.random.seed(seed)
random.seed(seed)
## 初始化坐标 (第一个点是基地的起点,起点的坐标是 0,0 )
data = [[0, 0]]
for i in range(num_city - 1):
while True:
x = np.random.randint(-250, 250)
y = np.random.randint(-250, 250)
if x != 0 or y != 0:
break
data.append([x, y])
data = np.array(data)
# 关键:有N架无人机,则再增加N-1个`点` (坐标是起始点),这些点之间的距离是inf
for d in range(num_drones - 1):
data = np.vstack([data, data[0]])
num_city += 1 # 增加欺骗城市
to_process_idx = [0]
# print("start point:", location[0])
for d in range(1, num_drones): # 1, ... drone-1
# print("added base point:", location[num_city - d])
to_process_idx.append(num_city - d)
model = GA(num_city=data.shape[0], num_total=20, data=data.copy())
Best_path, Best = model.run()
# print(Best_path)
iterations = model.iter_x
best_record = model.iter_y
# print(Best_path)
print(f"Best Path Length: {Best:.3f}")
plot_util.plot_results(Best_path, iterations, best_record)