古有牛顿在苹果树下被苹果砸中发现万有引力,三十年前有人观察蚂蚁觅食发明蚁群算法。蚁群算法是意大利学者Dorigo、Maniezzo等人于20世纪90年代看蚂蚁觅食发明的。蹲在地上看蚂蚁,也能发明新成果,曾经的我咋就光看蚂蚁,给蚂蚁的觅食捣蛋呢。这意大利的大兄弟在看蚂蚁觅食的时候呢,发现单个蚂蚁的行为比较简单,但是蚂蚁群却能体现一些智能行为,比如蚂蚁能在不同环境中,寻找最短的从蚂蚁窝到达食物的路径。经过进一步研究发现,蚂蚁在找食物的路径会留下记号(生物学称之为“信息素”),蚂蚁群里的蚂蚁会根据这个记号,也就是信息素行走,每个蚂蚁都做记号,经过一段时间后,整个蚂蚁群就会找到一个最短的到达食物的路径了。
蚁群算法具体的思想是什么呢?我是一只小蚂蚁,我要去找食儿。我带上几个兄弟一起走,一起去找事儿。可是食物在哪儿呢?我们也不知。那我们分头行动随机找,记得做标记。过了很久很久之后,我找到了食物,带着食物原路返回告诉大家我找到食物了。在蚂蚁窝里,我和兄弟们分享了找食物的路线,有的长,有的短。这些食物还不够,我们继续又出发。沿着大家新的路径去搬食物。有时候我会走错路,走了弯路;有时候,我走错路,却发现一条捷径。经过一次又一次的外出搬运食物,回到蚂蚁窝,我们找到了一个最方便的路径。这就是蚁群算法的基本思想。
蚁群算法可以很好的解决旅行商问题。所谓旅行商问题,是存在一些城市,旅行商如何从某个城市出发,经过所有城市后返回出发城市的最短路径。
下面用旅行商问题来继续阐述蚁群算法。
1、首先有一组蚂蚁,也就是小蚂蚁和它的兄弟们,从某个城市出发,完成一个城市遍历的路径。
2、当某个小蚂蚁站在人生的十字路口,哦不,是城市交叉路况,那么必须要走向一个城市,如果某个城市越远,那么将更有可能先不走。比如,当小蚂蚁站在天津,有一条路上北京,有一条路去深圳,那么小蚂蚁最有可能先踏上进京之路,因为近啊。
3、当某个城市边际上的记号信息素越多,则更有可能被选中。比如在天津,如果去深圳上记号信息素多,则可能先去深圳,尽管进京之路比较短,这是其他蚂蚁兄弟的记号表示的,相信蚂蚁兄弟。
4、每次回到出发点,蚂蚁兄弟标记在路上的记号信息素会挥发。
这里有两个关键点,即下一个城市的选择和信息素的更新。
现在给出一些数字表示:
城市数量为M,蚁群规模(蚂蚁数量)m、信息素重要程度因子α、启发函数重要程度因子β,即启发式信息的相对重要程度、信息素会发银子ρ、信息素释放总量Q、最大迭代次数iter_max、迭代次数初值iter=1。
小蚂蚁现在位于城市 i i i,到其他城市 j j j的距离为 d i j d_{ij} dij,信息素浓度为 p i j p_{ij} pij,那么小蚂蚁朝哪个城市前进呢。从当前城市 i i i到城市 j j j的概率是 q i j = p i j α ∗ ( 1 / d i j ) β q_{ij}=p^\alpha_{ij}*(1/d_{ij})^\beta qij=pijα∗(1/dij)β。
现在小蚂蚁知道了到其他城市的概率,可是该走哪条路呢。小蚂蚁拿出勇闯天涯的雪花啤酒瓶放在地上转个圈,瓶口指向哪个城市就去哪个城市吧。这就是轮盘对赌。把每个城市的转移概率除以所有城市的转移概率之和,那就是该城市被选中的概率,然后随机一个0到1的数,该随机数落到哪个区间就被选中。现在有ABCDE四个城市可以选择,那么可以看到下图里,D城市在所有城市中占据的分量最大,则最有可能被选中。可以参考这里轮盘对赌
当小蚂蚁走完所有的城市,那就对应着一条路径。蚂蚁兄弟也有搜索路径。根据小蚂蚁和蚂蚁兄弟的探路结果,更新下蚂蚁群体对路径的认识,那就是信息素的更新。两个城市之间的信息素更新公式为
p i j = ρ ∗ p i j + Q / s u m p_{ij}=\rho*p_{ij}+Q/sum pij=ρ∗pij+Q/sum
其中sum是每个蚂蚁走过的距离之和。
我们用python的GUI模块Tkinter来验证我们的小蚂蚁是怎么寻找最优路径的。python代码如下:
# -*- coding: utf-8 -*-
# @Time : 2019/12/25 16:23
# @Author : HelloWorld!
# @FileName: ant.py
# @Software: PyCharm
# @Operating System: Windows 10
# @Python.version: 3.6
#城市数量为M,
# 蚁群规模(蚂蚁数量)m、
# 信息素重要程度因子α、
# 启发函数重要程度因子β,即启发式信息的相对重要程度、
# 信息素会发银子ρ、
# 信息素释放总量Q、
# 最大迭代次数iter_max、迭代次数初值iter=1。
import random
import copy
import sys
import math
import tkinter
import threading
from functools import reduce
(ALPHA,BETA,RHO,Q)=(1,2,0.5,100)
#城市数,小蚂蚁个数
(city_num,ant_num)=(50,50)
#城市坐标
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=[[0 for col in range(city_num)] for raw in range(city_num)]
print(distance_graph)
pheromone_graph=[[1 for col in range(city_num)] for raw in range(city_num)]
#------------------小蚂蚁好多个,有灵魂,值得用一个类表示----------------------
class Ant():
def __init__(self,ID):
self.ID=ID #蚂蚁公民怎么能没有身份证呢。
self.__clean_data()#小蚂蚁初始化出生地
def __clean_data(self):
self.path=[]#小蚂蚁的路径
self.total_distance=0 #小蚂蚁当前路径的总距离
self.move_count=0 #小蚂蚁的移动次数
self.current_city=-1 #小蚂蚁的当前所在城市
self.open_table_city=[True for i in range(city_num)] #标记哪个城市去过,哪个没去过;也称为禁忌表
city_index=random.randint(0,city_num-1)#随机初始化出生地
self.current_city=city_index
self.path.append(city_index)
self.open_table_city[city_index]=False
self.move_count=1
#小蚂蚁如何选择下一个城市
def __choice_next_city(self):
next_city=-1
select_citys_prob=[0 for i in range(city_num)]#去下一个城市的概率
total_prob=0
#小蚂蚁掐指一算,去下一个城市的概率
for i in range(city_num):
if self.open_table_city[i]: #如果下一个城市没去过,还可以去
try:
#选中概率:与信息素浓度呈正比,与距离呈反比
select_citys_prob[i]=pow(pheromone_graph[self.current_city][i],ALPHA)*\
pow(1/distance_graph[self.current_city][i],BETA)
total_prob+=select_citys_prob[i]
except ZeroDivisionError as e:
print('Ant ID: {ID}, current city: {current}, target city: {target}'.
format(ID=self.ID,current=self.current_city,target=i))
sys.exit(1)
#勇闯天涯啤酒瓶,轮盘对赌
if total_prob>0:
#产生随机概率
temp_prob=random.uniform(0,total_prob)
for i in range(city_num):
if self.open_table_city[i]:
temp_prob-=select_citys_prob[i]
if temp_prob<0:
next_city=i
break
#如果没有从轮盘对赌中选出,则顺序选择一个未访问城市
if next_city==-1:
for i in range(city_num):
if self.open_table_city[i]:
next_city=i
break
if next_city==-1:
next_city=random.randint(0,city_num-1)
while False==self.open_table_city[next_city]:
next_city=random.randint(0,city_num-1)
#返回下一个城市序号
return next_city
#计算路径总距离
def __cal_total_distance(self):
temp_distance=0
for i in range(1,city_num):
start, end =self.path[i],self.path[i-1]
temp_distance+=distance_graph[start][end]
#回路
end=self.path[0]
temp_distance+=distance_graph[start][end]
self.total_distance=temp_distance
#小蚂蚁移动操作
def __move(self,next_city):
self.path.append(next_city)
self.open_table_city[next_city]=False
self.total_distance+=distance_graph[self.current_city][next_city]
self.current_city=next_city
self.move_count+=1
#小蚂蚁踏上寻路之旅
def search_path(self):
#初始化数据
self.__clean_data()
#搜索路径,遍历完所有城市为止
while self.move_count<city_num:
next_city=self.__choice_next_city()
self.__move(next_city)
#计算路径总长度
self.__cal_total_distance()
# 搜索路径,一直到遍历完所有城市路径
#------------------------------小蚂蚁来解决TSP问题--------------------------------------------------
class TSP():
def __init__(self,root,width=800,height=600,n=city_num):
self.root=root
self.width=width
self.height=height
#城市数目
self.n=n
#tkinter 来画布
self.canvas=tkinter.Canvas(root,width=self.width,height=self.height,bg= "#EBEBEB", xscrollincrement=1,yscrollincrement=1)
self.canvas.pack(expand=tkinter.YES,fill=tkinter.BOTH)
self.title("TSP蚁群算法(n:初始化 e:开始搜索 s:停止搜索 q:退出程序)")
self.__r = 5
self.__lock = threading.RLock() # 线程锁
self.__bindEvents()
self.new()
#计算城市之间的距离,欧拉距
for i in range(city_num):
for j in range(city_num):
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] = float(int(temp_distance + 0.5))
# 按键响应程序
def __bindEvents(self):
self.root.bind("q", self.quite) # 退出程序
self.root.bind("n", self.new) # 初始化
self.root.bind("e", self.search_path) # 开始搜索
self.root.bind("s", self.stop) # 停止搜索
# 更改标题
def title(self, s):
self.root.title(s)
# 初始化
def new(self, evt=None):
# 停止线程
self.__lock.acquire()
self.__running = False
self.__lock.release()
self.clear() # 清除信息
self.nodes = [] # 节点坐标
self.nodes2 = [] # 节点对象
# 初始化城市节点
for i in range(len(distance_x)):
# 在画布上随机初始坐标
x = distance_x[i]
y = distance_y[i]
self.nodes.append((x, y))
# 生成节点椭圆,半径为self.__r
node = self.canvas.create_oval(x - self.__r,
y - self.__r, x + self.__r, y + self.__r,
fill="#ff0000", # 填充红色
outline="#000000", # 轮廓白色
tags="node",
)
self.nodes2.append(node)
# 显示坐标
self.canvas.create_text(x, y - 10, # 使用create_text方法在坐标(302,77)处绘制文字
text='(' + str(x) + ',' + str(y) + ')', # 所绘制文字的内容
fill='black' # 所绘制文字的颜色为灰色
)
# 顺序连接城市
# self.line(range(city_num))
# 初始城市之间的距离和信息素
for i in range(city_num):
for j in range(city_num):
pheromone_graph[i][j] = 1.0
self.ants = [Ant(ID) for ID in range(ant_num)] # 初始蚁群
self.best_ant = Ant(-1) # 初始最优解
self.best_ant.total_distance = 1 << 31 # 初始最大距离
self.iter = 1 # 初始化迭代次数
# 将节点按order顺序连线
def line(self, order):
# 删除原线
self.canvas.delete("line")
def line2(i1, i2):
p1, p2 = self.nodes[i1], self.nodes[i2]
self.canvas.create_line(p1, p2, fill="#000000", tags="line")
return i2
# order[-1]为初始值
reduce(line2, order, order[-1])
# 清除画布
def clear(self):
for item in self.canvas.find_all():
self.canvas.delete(item)
# 退出程序
def quite(self, evt):
self.__lock.acquire()
self.__running = False
self.__lock.release()
self.root.destroy()
print(u"\n程序已退出...")
sys.exit()
# 停止搜索
def stop(self, evt):
self.__lock.acquire()
self.__running = False
self.__lock.release()
#开始搜索,小蚂蚁开始行动
# 开始搜索
def search_path(self, evt=None):
# 开启线程
self.__lock.acquire()
self.__running = True
self.__lock.release()
while self.__running:
#遍历每一只蚂蚁
for ant in self.ants:
ant.search_path() #小蚂蚁ant完成一个路径搜索
#我们的小蚂蚁是不是找到比当前最优的路径了
if ant.total_distance<self.best_ant.total_distance:
#更新最优
self.best_ant=copy.deepcopy(ant)
# 更新信息素
self.__update_pheromone_gragh()
print(u"迭代次数:", self.iter, u"最佳路径总距离:", int(self.best_ant.total_distance))
# 连线
self.line(self.best_ant.path)
# 设置标题
self.title("TSP蚁群算法(n:随机初始 e:开始搜索 s:停止搜索 q:退出程序) 迭代次数: %d" % self.iter)
# 更新画布
self.canvas.update()
self.iter += 1
# 更新信息素
def __update_pheromone_gragh(self):
# 获取每只蚂蚁在其路径上留下的信息素
temp_pheromone = [[0.0 for col in range(city_num)] for raw in range(city_num)]
for ant in self.ants:
for i in range(1, city_num):
start, end = ant.path[i - 1], ant.path[i]
# 在路径上的每两个相邻城市间留下信息素,与路径总距离反比
temp_pheromone[start][end] += Q / ant.total_distance
temp_pheromone[end][start] = temp_pheromone[start][end]
# 更新所有城市之间的信息素,旧信息素衰减加上新迭代信息素
for i in range(city_num):
for j in range(city_num):
pheromone_graph[i][j] = pheromone_graph[i][j] * RHO + temp_pheromone[i][j]
# 主循环
def mainloop(self):
self.root.mainloop()
# ----------- 程序的入口处 -----------
if __name__ == '__main__':
TSP(tkinter.Tk()).mainloop()
初始的城市分布情况如图所示:
迭代30次左右的情况
600次左右的情况
蚁群算法广泛应用在TSP(商旅问题)路径优化问题 调度问题 着色问题 聚类分析 网络路由 数据挖掘 工业PCB 板
其它组合优化。
蚁群算法的优点缺点:
具有较强的鲁棒性、优良的分布式计算机制和正反馈机制、易于与其他方法相结合等优点,缺点是求解时间长,初始信息素匮乏,容易出现停滞现象。
参考文献https://blog.csdn.net/weixin_39383896/article/details/101568241