import tkinter
from functools import reduce
import numpy as np
import random
import math
"""
为了解决蚁群算法前期因缺乏信息素因素导致的收敛缓慢
利用了遗传算法前期快速收敛的特性 继续前期的遗传变异
旨在若干组的优秀解 为后面蚁群算法的初始化信息素浓度作一个很好的分布
author:sugarMei
date:2020 5-30
version:1.0
language:python
"""
'''
alpha:信息素影响的强度大小
beta:可见度影响的强度大小
rho:信息素挥发因子
q:常数 用于计算每次一次遍历后的信息素强度变化程度
eta:从i城市到j城市的可见度
distance_graph:城市i到j的距离dij
pheromone:信息素矩阵 pij表示城市i到j城市的边的信息素的大小
path:路径表 pj表示第j只蚂蚁的路径表
length:记录一次循环中每个蚂蚁的路径长度li
prob:交叉概率
mutationProb:变异概率
population:种群规模
'''
# 初始化GA参数
prob, mutationProb, population, gaIter, MaxGaIter = 0.8, 0.08, 200, 0, 100
# 初始化ACA参数
alpha, beta, rho, q, generation, generations = 1, 2, 0.4, 100, 0, 200
cities, ants = 50, 75
distance_x = [
178, 272, 176, 171, 650, 499, 267, 703, 408, 437, 491, 74, 532,
416, 626, 42, 271, 359, 163, 508, 229, 576, 147, 560, 35, 714,
757, 517, 64, 314, 675, 690, 391, 628, 87, 240, 705, 699, 258,
428, 614, 36, 360, 482, 666, 597, 209, 201, 492, 294]
distance_y = [
170, 395, 198, 151, 242, 556, 57, 401, 305, 421, 267, 105, 525,
381, 244, 330, 395, 169, 141, 380, 153, 442, 528, 329, 232, 48,
498, 265, 343, 120, 165, 50, 433, 63, 491, 275, 348, 222, 288,
490, 213, 524, 244, 114, 104, 552, 70, 425, 227, 331]
distance_graph = np.zeros((cities, cities))
# 计算城市之间的距离
for i in range(cities):
for j in range(cities):
temp_distance = pow((distance_x[i] - distance_x[j]), 2) + pow((distance_y[i] - distance_y[j]), 2)
temp_distance = pow(temp_distance, 0.5)
distance_graph[i][j] = temp_distance
# 可见度矩阵
eta = 1.0 / (distance_graph + np.diag([1e10] * cities))
path = np.zeros((ants, cities), dtype=np.int)
# 最佳路径
best_path = []
best_length = np.inf
# 存放GA算法的输出:若干组优秀解
gaOutput = np.zeros((population, cities))
# 适应度值函数
def myLength(index_path):
fit = 0
for i in range(cities - 1):
tmp = distance_graph[index_path[i]][index_path[i + 1]]
fit += tmp
fit += distance_graph[index_path[-1]][index_path[0]]
return fit
# GA
# 初始化种群
pops = np.zeros((population, cities), dtype=np.int)
temp = np.arange(cities)
for i in range(population):
np.random.shuffle(temp)
pops[i] = temp
# 交叉函数
def cross(pop):
numbers = np.uint8(population * prob) - 1
if numbers % 2 != 0:
numbers += 1
index = random.sample(range(1, population), numbers)
new_pops1 = np.zeros((population, cities), dtype=np.int)
# 确保精英不会进去交配
new_pops1[0] = pop[np.argmin(fits)]
# 将不需要交叉的染色体直接复制到新的种群中
for i in range(1, population):
if not index.__contains__(i):
new_pops1[i] = pop[i]
# 交叉
j = 0
while j < numbers:
# w需要交叉的位数
w = 0
if cities < 10:
w = cities
elif ((cities / 10) - np.floor(cities / 10)) >= np.random.random() and cities > 10:
w = math.ceil(cities / 10) + 10
else:
w = math.floor(cities / 10) + 10
p = np.random.randint(0, cities - w + 1)
for i in range(w):
x = pop[index[j + 1]].tolist().index(pop[index[j]][p + i - 1])
y = pop[index[j]].tolist().index(pop[index[j + 1]][p + i - 1])
# exchange
pop[index[j + 1]][p + i - 1], pop[index[j]][p + i - 1] = pop[index[j]][p + i - 1], \
pop[index[j + 1]][p + i - 1]
pop[index[j + 1]][x], pop[index[j]][y] = pop[index[j]][y], \
pop[index[j + 1]][x]
# 等待所有位置交叉完成 再重新赋值
new_pops1[index[j]] = pop[index[j]]
new_pops1[index[j + 1]] = pop[index[j + 1]]
j += 2
return new_pops1
# 变异函数
def mutation(per_pop):
# 生成两个变异位置
cross_points = np.random.randint(0, cities, 2)
cross_point_1, cross_point_2 = cross_points[0], cross_points[1]
# 交换位置
per_pop[cross_point_1], per_pop[cross_point_2] = per_pop[cross_point_2], per_pop[cross_point_1]
return per_pop
# 选择函数
def select(pop, fit):
prob_fit = fit / sum(fit)
cum_prob = np.cumsum(prob_fit)
new_pop = np.zeros((population, cities))
# fitness最小的个体被保留 必定进入下一代
new_pop[0] = pop[np.argmin(fit)]
for i in range(1, population):
tmp = np.random.random()
for j in range(population):
# 被选中
if tmp < cum_prob[j]:
new_pop[i] = pop[j]
break
return new_pop.astype(int)
def fitness(l, m, maxlen, minlen):
fit = np.zeros(population)
for i in range(len(l)):
fit[i] = (1 - (l[i] - minlen) / (maxlen - minlen + 0.001)) ** m
return fit
m = 3
# 主循环
while gaIter < MaxGaIter:
length = np.zeros(population)
# 计算适应度值
for i in range(population):
length[i] = myLength(pops[i])
# 归一化
maxlen = max(length)
minlen = min(length)
fits = fitness(length, m, maxlen, minlen)
# 选择
pops = select(pops, fits)
# 交叉操作
pops = cross(pop=pops)
# 变异
for i in range(population):
if mutationProb > np.random.random():
pops[i] = mutation(pops[i])
gaIter += 1
# 输出所有的路径长度
# print(pops)
for a in pops:
print(myLength(a))
gaOutput = pops
pheromone = np.zeros((cities, cities))
# 每一条dij和dji路径 出现一次加k的信息素浓度
#
# k=0.6
# k = q / length.mean()
for i in range(int(population)):
for j in range(cities - 1):
pheromone[gaOutput[i][j]][gaOutput[i][j + 1]] += q / length[i]
pheromone[gaOutput[i][j + 1]][gaOutput[i][j]] += q / length[i]
pheromone[gaOutput[i][-1]][gaOutput[i][0]] += q / length[i]
pheromone[gaOutput[i][0]][gaOutput[i][-1]] += q / length[i]
print(pheromone)
# 根据gaOutput 初始化信息素矩阵
# 画图
root = tkinter.Tk()
canvas = tkinter.Canvas(
root,
width=800,
height=600,
bg="#EBEBEB", # 背景白色
xscrollincrement=1,
yscrollincrement=1
)
canvas.pack(expand=tkinter.YES, fill=tkinter.BOTH)
r = 5
nodes = [] # 节点坐标
nodes2 = [] # 节点对象
# 初始化城市节点
filename = tkinter.PhotoImage(file="/Users/sugarmei/PycharmProjects/sugarmei/es/city.png")
for i in range(len(distance_x)):
# 在画布上随机初始坐标
x = distance_x[i]
y = distance_y[i]
nodes.append((x, y))
# 生成节点椭圆,半径为self.__r
node = canvas.create_image(x, y, image=filename, tags="node")
# node = canvas.create_oval(x - r,
# y - r, x + r, y + r,
# fill="#ff0000", # 填充红色
# outline="#000000", # 轮廓白色
# tags="node",
# )
nodes2.append(node)
# 显示坐标
canvas.create_text(x, y - 20, # 使用create_text方法在坐标(302,77)处绘制文字
text='', # 所绘制文字的内容
fill='black' # 所绘制文字的颜色为灰色
)
def title(s):
root.title(s)
# 将节点按order顺序连线
def line(order):
# 删除原线
canvas.delete("line")
def line2(i1, i2):
p1, p2 = nodes[i1], nodes[i2]
canvas.create_line(p1, p2, fill="#000000", tags="line")
return i2
# order[-1]为初始值
reduce(line2, order, order[-1])
print(eta)
while generation < generations:
# 初始化蚂蚁位置
if ants < cities:
path[:, 0] = np.random.permutation(cities)[:ants]
else:
path[:cities, 0] = np.random.permutation(cities)[:]
path[cities:, 0] = np.random.permutation(cities)[:ants - cities]
length = np.zeros(ants)
# 计算第k只蚂蚁从i城市到达j城市的概率
for k in range(ants):
visited = path[k, 0]
unvisited = set(range(cities))
unvisited.remove(visited)
for i in range(1, cities):
l_unvisited = list(unvisited)
prob_next_city = np.zeros(len(l_unvisited), dtype=np.float64)
for j in range(len(l_unvisited)):
prob_next_city[j] = pow(pheromone[visited][l_unvisited[j]], alpha) * pow(eta[visited][l_unvisited[j]],
beta)
# 解决精度问题
if prob_next_city[j] == 0.0:
prob_next_city[j] = 0.00001
prob_next_city = prob_next_city / np.sum(prob_next_city, dtype=float)
prob_next_city = np.cumsum(prob_next_city)
temp_prob = np.random.random()
next_city = -1
# 轮盘赌算法
for p in range(len(prob_next_city)):
# 第p个城市赌成功
if not math.isnan(prob_next_city[p]):
if prob_next_city[p] >= temp_prob:
next_city = l_unvisited[p]
break
unvisited.remove(next_city)
path[k, i] = next_city
length[k] += distance_graph[visited][next_city]
visited = next_city
length[k] += distance_graph[visited][path[k, 0]]
# 调整最短长度和最佳路径
if length.min() < best_length:
best_length = length.min()
best_path = path[length.argmin()]
line(best_path)
root.title("ACO 第 " + str(generation) + " 次迭代" + " 当前最短长度:" + str(best_length))
root.update()
# time.sleep(10)
# 本轮遍历一次全部城市结束 调整信息素强度
tmp_pheromone = np.zeros((cities, cities))
for i in range(ants):
for j in range(cities - 1):
# 使用了蚁环算法
# length[i]为第i只蚂蚁当前次遍历的路径长度 作为整体信息进行信息素的更新
tmp_pheromone[path[i, j]][path[i, j + 1]] += q / length[i]
# 从j城市到i城市的距离dji与dij一致 因此信息素浓度一致
if tmp_pheromone[path[i, j + 1]][path[i, j]] < tmp_pheromone[path[i, j]][path[i, j + 1]]:
tmp_pheromone[path[i, j + 1]][path[i, j]] = tmp_pheromone[path[i, j]][path[i, j + 1]]
tmp_pheromone[path[i, cities - 1]][path[i, 0]] += q / length[i]
# 蚁密算法
# tmp_pheromone[path[i, j]][path[i, j + 1]] += q
# tmp_pheromone[path[i, cities - 1]][path[i, 0]] += q
# 蚁量算法 与当前城市之间距离成反比
# tmp_pheromone[path[i, j]][path[i, j + 1]] += q / distance_graph[path[i, j]][path[i, j + 1]]
# tmp_pheromone[path[i, cities - 1]][path[i, 0]] += q / distance_graph[path[i, cities - 1]][path[i, 0]]
# 更新从i到j城市的路径的信息素浓度
pheromone = (1 - rho) * pheromone + tmp_pheromone
generation += 1
print("当前迭代次数:", generation, "当前最短长度:", best_length)
print("迭代次数:", generations)
print("最佳路径:", best_path)
print("最短长度:", best_length)
遗传-蚁群算法python实现
最新推荐文章于 2024-08-12 20:36:23 发布