文章目录
- 蚁群算法简单介绍
- 蚁群算法概念
- 转移概率
- 算法流程
- 信息素更新
- 信息素更新公式
- 三种信息素更新模型
- 蚁周模型
- 蚁密模型
- 蚁量模型
- TSP问题简介
- 城市坐标
- 编码
- 目标函数
- 编程实现
- 编程思路
- 代码
- 求解路径可视化
蚁群算法简单介绍
蚁群算法 模仿蚂蚁集体寻径行为 提出的算法,属于种群启发式搜索算法。算法通过蚂蚁在路径上留下信息素和大量蚂蚁的引入,诱使蚂蚁在选择 路径时 容易对更优的路径进行选择。
蚂蚁的选择属于随机选择,目的地路径上的信息素和长度决定了选择该路径的可能性(概率),在路径长度固定的情况下,路径上信息素越多,蚂蚁选择该路径的概率越大。
从而形成正反馈的效果,最终致使算法收敛。
蚁群算法概念
概念 | 符号 | 解释 |
人工蚂蚁 | ant | 搜索解的单元,本身具有记忆(存储)解的能力,自身的行为就是在探索可行解 |
禁忌表 | tabu | 记录蚂蚁已经走过的城市(体现蚂蚁的记忆功能) |
信息素 | 蚂蚁在两城市路径之间留下的标记,可以理解为一种权重 | |
信息素蒸发系数 | ρ | 反映信息素在路径上随时间的蒸发速度(在算法中是每一次迭代之后,信息素丢失的比例) |
转移概率 | Pij | 蚂蚁从 i 城市向 j 城市 转移时所依据的概率阈值 |
转移概率
第k只蚂蚁,Sk是第k只蚂蚁没有去过的城市集合,从城市 i 向城市 j 转移的概率定义为
τ是信息素,表示群体探索解的经验
η是路径的度量,一般采用距离的导数,表示先验经验
该概率其实就是通往城市 j 路径的权重在所有可选路径权重中所占的比例
比如下图中蚂蚁从城市i转移至城市j的概率,Sk = {h,j,l}
算法流程
信息素更新
信息素更新公式
假设m只蚂蚁,n座城市,t时刻 到 t+n时刻信息素的更新
Δτ_{ij}^{k}表示第k只蚂蚁从城市i到城市j 在路径上留下的信息素
τ_{ij}是城市i到城市j路径上的信息素
ρ的大小影响着算法的收敛速度和搜索能力
ρ越小算法的全局搜索和随机搜索能力越好,但是算法也越不容易收敛
ρ越大算法的全局搜索能力减弱,算法收敛能力增强,但同时算法也容易陷入局部最优解
三种信息素更新模型
Lk 表示 蚂蚁走完全部30个城市之后的路径长度,dij是城市i到城市j之间的路径长度,Q为常数
蚁周模型
蚁密模型
蚁量模型
TSP问题简介
TSP,全称Travelling Salesman Problem (旅行商问题)
给出城市坐标,求解旅行商 如何每个城市仅经过一次就可以遍历所有城市,而且要求路径长度最短
城市坐标
城市 | 坐标 | 城市 | 坐标 | 城市 | 坐标 |
0 | (18,54) | 10 | (71,44) | 20 | (7,64) |
1 | (87,76) | 11 | (64,60) | 21 | (22,60) |
2 | (74,78) | 12 | (68,58) | 22 | (25,62) |
3 | (71,71) | 13 | (83,69) | 23 | (62,32) |
4 | (25,38) | 14 | (58,69) | 24 | (87,7) |
5 | (58,35) | 15 | (54,62) | 25 | (91,38) |
6 | (4,50) | 16 | (51,67) | 26 | (83,46) |
7 | (13,40) | 17 | (37,84) | 27 | (41,26) |
8 | (18,40) | 18 | (41,94) | 28 | (45,21) |
9 | (24,42) | 19 | (2,99) | 29 | (44,35) |
编码
城市编号从0至29,而且需要记录30个城市的顺序,所以采用实数编码
具体形式如下:
编码空间中的一个编码,也就是解空间中的一个解,也是种群中的一个个体
用向量表示
path = [20, 1, 3, 7, 14, 25, 10, 2, 27, 24, 8, 0, 16, 26, 22, 5, 18, 28, 4, 19, 13, 12, 11, 21, 6, 23, 29, 9, 17, 15]
该向量含义:
从城市20开始,经历城市1,3,7, 14, 25, 10, 2, 27, 24, 8, 0, 16, 26, 22, 5, 18, 28, 4, 19, 13, 12, 11, 21, 6, 23, 29, 9, 17 ,到城市15再回到20结束
目标函数
将路径长度函数作为目标函数,两城市间 距离按照欧式距离(向量差的二范数)计算
定义城市 i 和城市 j 之间的距离,用Xi代表第i个城市的坐标,Xj代表第j个城市的坐标
定义推销员走过的路径总长度,path[i]代表路径中经过的第i个城市的编号,数据可以参考上一段中编码的内容
path作为一个可行解
编程实现
编程思路
给每一只蚂蚁定义一个字典储存信息,三个键tabu(禁忌表)、Lk(剩余城市)和L(当前路径长度)
字典类型dict,三个键数据类型str,tabu的值数据类型是list,Lk的值的数据类型是list,L的值的数据类型是float
城市间距离先计算好,之后计算距离时仅加运算以减小不必要的消耗
信息素和距离计算时,从信息素储存矩阵和城市距离矩阵中取出,存入数据类型为numpy.array的向量中(主要是方便计算转移概率时的幂运算)
代码
jupyter文件代码
In[1]:
import numpy as np
import random
from copy import deepcopy
In[2]:
#位置信息
locations = [
[18,54],[87,76],[74,78],[71,71],[25,38],
[58,35],[4,50],[13,40],[18,40],[24,42],
[71,44],[64,60],[68,58],[83,69],[58,69],
[54,62],[51,67],[37,84],[41,94],[2,99],
[7,64],[22,60],[25,62],[62,32],[87,7],
[91,38],[83,46],[41,26],[45,21],[44,35]
]
In[3]:
#快速幂运算
def pow_mat(mat,p):
tmp = deepcopy(mat)
if p==1:
return tmp
j = 1
while j<p:
j = j<<1
tmp *= tmp
if p-j==1:
tmp *= mat
break
return tmp
In[4]:
def dist(x,y):
return round(((x[0]-y[0])**2+(x[1]-y[1])**2)**0.5,2)
#轮盘赌选择实现
def choice(rs):
p = random.random()
i = 0
while p>0:
p -= rs[i]
i += 1
return i-1
In[5]:
#城市间距离矩阵
cities_dis = np.array([0.1 for i in range(30)]*30).reshape(30,30)
for i in range(30):
for j in range(i+1,30):
tmp = dist(locations[i],locations[j])
cities_dis[i][j] = tmp
cities_dis[j][i] = tmp
#城市间距离倒数矩阵
cities_dis_recip = 1/cities_dis
In[6]:
#蚂蚁决策
def decision(city_now,jk,alp,beta):
global cities_phernomones
global cities_dis_recip
tmp_recip = []
tmp_pheno = []
for item in jk:
tmp_recip.append(cities_dis_recip[city_now][item])
tmp_pheno.append(cities_phernomones[city_now][item])
tmp_recip = np.array(tmp_recip)
tmp_pheno = np.array(tmp_pheno)
tmp_recip = pow_mat(tmp_recip,beta)
tmp_pheno = pow_mat(tmp_pheno,alp)
p = tmp_recip*tmp_pheno
s = np.sum(p)
p /= s
id = choice(p)
return jk[id]
In[7]:
#蚂蚁周游
def walk_cycle(ant,alpha,beta):
global cities_dis
while ant['Jk'] != []:
id = decision(ant['tabu'][-1],ant['Jk'],alpha,beta)
ant['Jk'].remove(id)
ant['L'] += cities_dis[ant['tabu'][-1],id]
ant['tabu'].append(id)
ant['L'] += cities_dis[ant['tabu'][-1],0]
In[8]:
#重置函数
def reset_ant(ant):
start = random.randint(0,29)
ant['tabu'] = [start]
ant['Jk'] = [i for i in range(30) if i!=start]
ant['L'] = 0
In[9]:
#初始化蚂蚁群
ants = []
for i in range(50):
start = random.randint(0,29)
tmp = {'tabu':[start],'Jk':[i for i in range(30) if i!=start],'L':0}
ants.append(tmp)
In[10]:
#参数
Np = 50
alpha = 1
beta = 3
Q = 1
rho = 0.1
In[11]:
G = 200
cities_order = []
#蚁周模型
#info_c = {}
#蚁密模型
#info_d = {}
#蚁量模型
info_q = {}
path_q = {}
best = 1e5
#城市间路径信息素矩阵
cities_phernomones = np.array([1 for i in range(30)]*30).reshape(30,30)
while G>0:
G -= 1
tmp = 1e5
#蚂蚁周游 并记录最优路径
for i in range(Np):
walk_cycle(ants[i],alpha,beta)
if ants[i]['L']<best:
best = ants[i]['L']
cities_order = ants[i]['tabu']
if ants[i]['L']<tmp:
tmp = ants[i]['L']
path_q[200-G] = deepcopy(ants[i]['tabu'])
info_q[200-G] = tmp
#信息素挥发
cities_phernomones = (1-rho)*cities_phernomones
#按照蚁周模型更新信息素
for i in range(Np):
for j in range(30):
m,n = ants[i]['tabu'][j-1],ants[i]['tabu'][j]
#蚁量模型
# cities_phernomones[m][n] += Q/cities_dis[m][n]
# cities_phernomones[n][m] += Q/cities_dis[m][n]
#蚁密模型
'''cities_phernomones[m][n] += Q
cities_phernomones[n][m] += Q'''
#蚁周模型
cities_phernomones[m][n] += Q/ants[i]['L']
cities_phernomones[n][m] += Q/ants[i]['L']
#清空蚂蚁禁忌表并随机开始城市
for i in range(Np):
reset_ant(ants[i])
In[12]:
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (20,8)
In[13]:
qun_x = list(info_q.keys())
qun_y = list(info_q.values())
plt.xlabel('G')
plt.ylabel('distance')
plt.title('G=200 Np={} alpha={} beta={} Q={} rho={} min_dist={}'.format(Np,alpha,beta,Q,rho,best))
plt.plot(qun_x,qun_y)
#plt.savefig('./result/ants/ants{}_{}_{}_{}_{}.png'.format(Np,alpha,beta,Q,rho))
Out[13]:
求解路径可视化