文章目录
- 蚁群算法
- 蚂蚁的基本变量
- 蚂蚁的优化流程
- 蚁群优化
- 验证与可视化
蚁群算法
蚁群算法是Colori A等人在1991年提出的,通过模仿蚂蚁觅食行为,抽象出信息素这一奖惩机制,从而赋予蚂蚁智能Agent的身份,使之得以在最佳路线问题中大展身手。
现有一群蚂蚁想从 A 0 A_0 A0点走到 A n A_n An点,假设两点中间有多条路径可以选择,蚂蚁不知道哪条路更近,所以最开始无论选择哪条路,机会都是相等的。但是,当某一条路有蚂蚁走过之后,就会留下信息素,如果每一只蚂蚁的速度是恒定的,那么路程越短,蚂蚁在这条路上往返也就越快,反过来说,单位时间内,越短的路会有更多的蚂蚁走过,从而留下的信息素就会越多,这就是蚁群算法的基本原理了。
当不同路径均留下信息素后,蚂蚁们再行选择路线就不是等概率的了,他们会有更大的概率选择信息素更浓的路线。随着时间的推演,信息素也会挥发,从而蚂蚁们不断更新他们的路线,这个过程就是蚁群优化(Ant Clony Optimization, ACO)。
蚂蚁的基本变量
很显然,蚁群算法的前提是来一群蚂蚁对象,而蚂蚁作为一个智能体,至少要有一些记忆,需要记住以下内容
- 城市个数
- 城市地图
- 每个城市留下的信息素
- 已经走过和尚未到达的城市
- 移动的步数
- 已经走过的距离
而经过初始化之后,这个蚂蚁必须得来到某个城市,所以需要一个随机选择城市的函数,下面就是一个蚂蚁所必备的初始化流程
import numpy as np
from functools import reduce
class Ant(object):
def __init__(self, nCity, graph, pheromone):
self.nCity = nCity # 城市数
self.graph = graph # 城市地图
self.pheromone = pheromone # 信息素地图
self.cityEnabled = [True] * nCity # 尚未到达的城市标记
self.nMove = 0 # 移动步数
self.dTotal = 0.0 # 已经走过的距离
self.initData() # 初始化出生点
# 随机选择城市
def randCity(self):
return np.random.randint(0,self.nCity-1)
# 初始化
def initData(self):
self.nowCity = self.randCity() # 随机选择一个城市
self.path = [self.nowCity] # 保存当前走过的城市
self.cityEnabled[self.nowCity] = False # 当前城市不再探索
self.nMove = 1 # 初始时的移动计数
蚂蚁的优化流程
某只蚂蚁在初始化之后,就要通过它敏锐的“嗅觉”选择下一个城市,直到走完所有城市为止。而选择城市时,需要用到信息素地图作为判断依据。信息素浓度为 p p p,两点距离为 d d d,则蚂蚁前往该城市的概率为
p α / d β p^\alpha/d^\beta pα/dβ
其中, α \alpha α和 β \beta β分别叫做信息素启发因子和期望启发因子,故而在Ant类中新建方法:
# 计算到达第i个城市的概率
def getOneProb(self, i):
ALPHA = 1.0
BETA = 2.0
dis = self.graph[self.nowCity][i]
phe = self.pheromone[self.nowCity][i]
# 计算移动到该城市的概率
if not self.cityEnabled[i]: return 0
else: return pow(phe, ALPHA) * pow(1/dis, BETA)
有了这个,就可以得到去所有城市的概率,然后随机则取
def choiceNext(self):
# 前往所有城市的概率
pSlct = [self.getOneProb(i) for i in range(self.nCity)]
pSum = np.cumsum(pSlct)
# 生成一个随机数,这个随机数在pSum中落入的区间就是选择的城市
pTemp = np.random.uniform(0.0, pSum[-1])
return np.searchsorted(pSum, pTemp)
在选中接下来前往的城市之后,就要动身前往
# 移动到新的城市
def moveTo(self, city):
self.path.append(city) # 添加目标城市
self.cityEnabled[city] = False # 目标城市不可再探索
# 总路程增加当前城市到目标城市的距离
self.dTotal += self.graph[self.nowCity][city]
self.nowCity = city # 更新当前城市
self.nMove += 1 # 移动次数
唯一需要注意的是,旅行商问题要求走一圈,也就是说,当走遍所有城市之后,要记得把第一个城市和最后一个城市的距离添加到全部路程当中,故其总的搜索流程如下
def run(self):
self.initData()
while self.nMove < self.nCity:
next_city = self.choiceNext()
self.moveTo(next_city)
self.dTotal += self.graph[self.path[0]][self.path[-1]]
return self.dTotal
至此,就实现了一个蚂蚁类。
蚁群优化
有了蚂蚁,那么接下来就要有蚁群,以及适用于蚁群算法的城市网点。设城市坐标序列为 x i x_i xi和 y i y_i yi,则根据这组坐标点的个数就是城市数nCity,而城市距离矩阵可表示为
G i j = ( x i − x j ) 2 + ( y i − y j ) 2 G_{ij} = \sqrt{(x_i-x_j)^2+(y_i-y_j)^2} Gij=(xi−xj)2+(yi−yj)2
另一方面,蚁群优化过程中,信息素更新至关重要,其值受到两个因素影响,一是蚂蚁走过会使之增加,二则是时间流逝会使之挥发,故而需要有一个挥发系数RHO。信息素更新函数如下
# 更新信息素
RHO = 0.5 # 信息素挥发系数
def updatePheromone(nCity, pheromone, ants):
# 初始化蚂蚁在两两城市间的信息素, 50行50列
temp = np.zeros([nCity, nCity])
# 遍历每只蚂蚁对象
for ant in ants:
for i in range(1, nCity): # 遍历该蚂蚁经过的每个城市
st, ed = ant.path[i-1], ant.path[i]
# 在两个城市间留下信息素,浓度与总距离成反比
temp[st, ed] += Q / ant.dTotal
temp[ed, st] = temp[st, ed] # 信息素矩阵轴对称
return pheromone * RHO + temp
至此,万事俱备,只欠东风,蚁群优化算法的主体程序如下
import copy
# xs, ys为城市的x和y坐标
# nAnts为蚂蚁个数, nIter为迭代次数
def aco(xs, ys, nAnts, nIter):
nCity = len(xs)
xMat, yMat = xs-xs.reshape(-1,1), ys-ys.reshape(-1,1)
graph = np.sqrt(xMat**2 + yMat**2)
pheromone = np.ones([nCity, nCity]) # 信息素矩阵
ants = [Ant(nCity, graph, pheromone) for _ in range(nAnts)]
best = Ant(nCity, graph, pheromone) # 初始化最优解
best.dTotal = np.inf
bestAnts = [] # 输出并保存
for i in range(nIter):
for ant in ants:
ant.pheromone = pheromone
ant.run()
# 与当前最优蚂蚁比较步行的总距离
if ant.dTotal < best.dTotal:
# 更新最优解
best = copy.deepcopy(ant)
print(f"{i},{best.dTotal}")
# 更新信息素
pheromone = updatePheromone(nCity, pheromone, ants)
bestAnts.append(best)
return bestAnts
验证与可视化
又到了激动人心的验证时刻。根据aco函数的输入参数来看,主要需要一组空间坐标,设置如下
# 每个城市的x和y坐标
xs = np.array([
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])
ys = np.array([
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])
xMat, yMat = xs-xs.reshape(-1,1), ys-ys.reshape(-1,1)
distance_graph = np.sqrt(xMat**2 + yMat**2)
最后main函数为
if __name__ == '__main__':
bAnts = aco(xs, ys, 50, 300)
index = bAnts[-1].path
index = index + [index[0]]
plt.plot(xs[index], ys[index], marker='*')
plt.tight_layout()
plt.show()
最终优化后的蚂蚁路线如下