import numpy as np
class AntColony:
def __init__(self, distances, n_ants, n_best, n_iterations, decay, alpha=1, beta=1):
# 初始化蚁群算法的参数
self.distances = distances # 城市间的距离矩阵
# 初始化信息素矩阵, 行列为输入的distances的行列, 每个信息素的初始值为1/distances的行数, 也就是说每行信息素的和都=1
self.pheromone = np.ones(self.distances.shape) / len(distances)
self.all_inds = range(len(distances)) # 城市索引, 列表=[0,1,2,3...len(distances)]
self.n_ants = n_ants # 蚂蚁数量
self.n_best = n_best # 选择的最佳路径数量, b_best=1
self.n_iterations = n_iterations # 迭代次数
self.decay = decay # 信息素衰减率
self.alpha = alpha # 信息素重要性因子
self.beta = beta # 启发式信息重要性因子
def run(self):
shortest_path = None # 当前迭代中的最短路径
all_time_shortest_path = ("placeholder", np.inf) # 全局最短路径, np.inf代表无穷大
for i in range(self.n_iterations):
all_paths = self.gen_all_paths() # 生成所有蚂蚁的路径
self.spread_pheronome(all_paths, self.n_best, shortest_path=shortest_path) # 传播信息素
shortest_path = min(all_paths, key=lambda x: x[1]) # 找到当前迭代中的最短路径
if shortest_path[1] < all_time_shortest_path[1]:
all_time_shortest_path = shortest_path # 更新全局最短路径
self.pheromone * self.decay # 信息素衰减
return all_time_shortest_path
def spread_pheronome(self, all_paths, n_best, shortest_path):
sorted_paths = sorted(all_paths, key=lambda x: x[1]) # 按路径长度排序
for path, dist in sorted_paths[:n_best]:
for move in path:
self.pheromone[move] += 1.0 / self.distances[move] # 更新信息素
def gen_path_dist(self, path):
total_dist = 0
for ele in path:
total_dist += self.distances[ele] # 计算路径总距离
return total_dist
def gen_all_paths(self):#形成所有蚂蚁的路径
all_paths = []
for i in range(self.n_ants):
path = self.gen_path(0) # 生成单个蚂蚁的路径
all_paths.append((path, self.gen_path_dist(path))) # 记录单个蚂蚁访问城市的路径和总距离
return all_paths
#记录单个蚂蚁访问所有城市的路径图
def gen_path(self, start):#start代表单个蚂蚁起始位置, 本例中所有蚂蚁都从0号位置开始访问
path = []
visited = set() #初始化一个空的集合
visited.add(start) # 标记起始城市为已访问
prev = start
for i in range(len(self.distances) - 1):
move = self.pick_move(self.pheromone[prev], self.distances[prev], visited) # 选择下一步移动
path.append((prev, move)) # 记录路径
prev = move
visited.add(move) # 标记城市为已访问
path.append((prev, start)) # 返回起始城市
return path
#从未被访问过的城市中随机选择一个城市访问, 返回被选择的未被访问过的城市的序号
def pick_move(self
, pheromone #初始化信息素矩阵, 行列为输入的distances的行列, 每个信息素的初始值为1/distances的行数, 也就是说每行信息素的和都=1
#self.pheromone = np.ones(self.distances.shape) / len(distances)
, dist #dist代表城市之间的距离矩阵
, visited #visited = set() # 初始化一个空的集合 visited.add(start) # 标记起始城市为已访问
):#蚂蚁选择下一步要移动的位置
# 创建信息素数组的副本, pheromone是一个数组,因为传参时传的是self.pheromone[prev], prev=0
pheromone = np.copy(pheromone)
#list(visited)是一个集合转成列表, 代表在将列表中的各个元素所在数组中指向的位置中的值设置为0
pheromone[list(visited)] = 0 # 已访问城市的信息素设为0
#pheromone ** self.alpha代表城市i到城市j之间的信息素浓度, alpha=1
#(1.0 / dist)代表启发式信息, 通常是距离的倒数, beta-1
row = pheromone ** self.alpha * ((1.0 / dist) ** self.beta) # 计算选择概率
norm_row = row / row.sum() # 归一化概率, 由于使用归一化, 所以在当前情况下, 除了值为0的元素(已访问过的城市)之外, 其余城市选择的概率相等
move = np_choice(self.all_inds #self.all_inds = range(len(distances)) # 城市索引, 列表=[0,1,2,3...len(distances)]
, 1, p=norm_row)[0] # 按概率选择下一个城市(未被选择过的城市)
return move
def np_choice(a, size, replace=True, p=None):#replace=True代表允许重复, size代表要选择的个数
return np.random.choice(a, size, replace, p)#从剩下的城市中随机选择一个城市
if __name__ == "__main__":
#城市之间的距离, 代表i号城市和j号城市之间的距离, 每一行代表i号城市与其它城市之间的距离
#例子: 第0行代表0号城市与0,1,2,3,4号城市之间的距离; 第1行代表1号城市与1,2,3,4,5号城市之间的距离. 是一个对称矩阵
distances = np.array([[np.inf, 2, 2, 5, 7],
[2, np.inf, 4, 8, 2],
[2, 4, np.inf, 1, 3],
[5, 8, 1, np.inf, 2],
[7, 2, 3, 2, np.inf]])
ant_colony = AntColony(distances, 3, 1, 100, 0.95, alpha=1, beta=2)
shortest_path = ant_colony.run()
print("shortest_path: {}".format(shortest_path))
05-01
2767
08-08
5718
08-23
175
03-28