蚁群算法
算法介绍
假如蚁群中所有蚂蚁的数量为m,所有城市之间的信息素用矩阵pheromone表示,最短路径为bestLength,最佳路径为bestTour。每只蚂蚁都有自己的内存,内存中用一个禁忌表(Tabu)来存储该蚂蚁已经访问过的城市,表示其在以后的搜索中将不能访问这些城市;还有用另外一个允许访问的城市表(Allowed)来存储它还可以访问的城市;另外还用一个矩阵(Delta)来存储它在一个循环(或者迭代)中给所经过的路径释放的信息素;还有另外一些数据,例如一些控制参数(α,β,ρ,Q)。
算法参数设置
蚂蚁数量(m):一般设置为目标数量的1.5倍。
信息素常量(Q):根据经验一般取值在[10,1000]。
最大迭代次数(t):一般选择[100,500]之间,取200次为优。
信息素因子(α):反映了在蚂蚁运动的过程中路径上积累的信息素的量在指导蚁群搜索中的相对重要程度,取值范围常在[1,4]。
启发函数因子(β):反映了启发式信息在指导一群搜索中的相对重要程度,蚁群寻优过程中先验性、确定性因素作用的强度,取值范围常在[0,5]。
信息素挥发因子(ρ):反映了信息素的消失水平,1-ρ反映了信息素的保持水平,取值范围通常在[0.2,0.5]。
路径构建
每只蚂蚁随机选择一个城市作为其出发城市,并维护一个记忆向量(禁忌表),用于存放该蚂蚁已经经过的城市。
在蚂蚁构建路径的每一步中,使用轮盘赌法选择下一步要到达的城市。
- i,j 分别代表起点和终点。
是起点 i 和终点 j 间距离的倒数,用于表示蚂蚁从 i 到 j 的能见度。(距离越小能见度更大) 是时间 t 时,由 i 到 j 的信息素浓度。 是尚未访问过的节点的集合。
信息素的更新
表示第t+1次循环后城市 i 到城市 j 上的信息素含量。- 1-ρ 表示信息素残留系数
表示新增信息素含量 = m只蚂蚁在城市 i 到 j 路径上留下的信息素总和。
而根据不同的规则,我们可以将蚁群算法分为三种模型:蚁周模型(Ant-Cycle)、蚁量模型(Ant-Quantity)、蚁密模型(Ant-Density)。
蚁周模型(Ant-Cycle)
蚁周模型的规则是:完成一次路径循环后,蚂蚁才释放信息素。
Lk 表示蚂蚁 k 所经过的路径之和。
蚁量模型(Ant-Quantity)
dk表示城市 i 和城市 j 间的距离。
蚁密模型(Ant-Density)
通常使用蚁周模型作为基础
迭代次数的判断
一次迭代是指m只蚂蚁都走完所有能走的城市,即存在m个搜索路径。后将所有的路径进行比较,选择出长度最短的路径,做出一次迭代的可视化结果,更新信息素。后将此路径和之前的最短的路径长度进行对比,同时迭代次数+1。
python代码示例
xxxxxxxxxx
# -*- coding: utf-8 -*-
import random
import copy
import time
import sys
import math
import tkinter # //GUI模块
import threading
from functools import reduce
# 参数
'''
ALPHA:信息启发因子,值越大,则蚂蚁选择之前走过的路径可能性就越大
,值越小,则蚁群搜索范围就会减少,容易陷入局部最优
BETA:Beta值越大,蚁群越就容易选择局部较短路径,这时算法收敛速度会
加快,但是随机性不高,容易得到局部的相对最优
'''
(ALPHA, BETA, RHO, Q) = (1.0, 2.0, 0.5, 100.0)
# 城市数,蚁群
(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.0 for col in range(city_num)] for raw in range(city_num)]
pheromone_graph = [[1.0 for col in range(city_num)] for raw in range(city_num)]
# ----------- 蚂蚁 -----------
class Ant(object):
# 初始化
def __init__(self, ID):
self.ID = ID # ID
self.__clean_data() # 随机初始化出生点
# 初始数据
def __clean_data(self):
self.path = [] # 当前蚂蚁的路径
self.total_distance = 0.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.0 for i in range(city_num)] # 存储去下个城市的概率
total_prob = 0.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.0 / 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.0:
# 产生一个随机概率,0.0-total_prob
temp_prob = random.uniform(0.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.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 ((self.open_table_city[next_city]) == False): # if==False,说明已经遍历过了
next_city = random.randint(0, city_num - 1)
# 返回下一个城市序号
return next_city
# 计算路径总距离
def __cal_total_distance(self):
temp_distance = 0.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(object):
def __init__(self, root, width=800, height=600, n=city_num):
# 创建画布
self.root = root
self.width = width
self.height = height
# 城市数目初始化为city_num
self.n = n
# tkinter.Canvas
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()
# 与当前最优蚂蚁比较
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__':
print(u"""
--------------------------------------------------------
程序:蚁群算法解决TPS问题程序
作者:许彬
日期:2015-12-10
语言:Python 2.7
--------------------------------------------------------
""")
TSP(tkinter.Tk()).mainloop()
MatLab算法实现(目前不是最优)
% 蚁群算法自我逻辑实现
clear all;
% 变量的初始化
Alpha = 2;
Beta = 3;
Rho = 0.3;
ant_num = 50;
Q = 500;
Iterator_Max = 200;
% 城市坐标初始化
City=[
1304 2312;
3639 1315;
4177 2244;
3712 1399;
3488 1535;
3326 1556;
3238 1229;
4196 1004;
4312 790;
4386 570;
3007 1970;
2562 1756;
2788 1491;
2381 1676;
1332 695;
3715 1678;
3918 2179;
4061 2370;
3780 2212;
3676 2578;
4029 2838;
4263 2931;
3429 1908;
3507 2367;
3394 2643;
3439 3201;
2935 3240;
3140 3550;
2545 2357;
2778 2826;
2370 2975
];
% 迭代信息的初始化
city_num= size(City,1);
city_distance = inf.*ones(city_num,city_num);
city_pheromones=ones(city_num,city_num); % 置为1表示无影响
ant_way = zeros(ant_num,city_num+1); % 多一个留给回来时记录
available_city=ones(ant_num,city_num); % 禁忌表
ant_distance = zeros(ant_num,1);
% 城市间的距离初始化
for i=1:city_num
for j=1:city_num
if i==j
city_distance(i,j)=eps; % 自己到自己的距离为0-但是需要求距离的倒数,所以取eps
else
city_distance(i,j)=((City(i,1)-City(j,1))^2+(City(i,2)-City(j,2))^2)^0.5;
end
city_distance(j,i)=city_distance(i,j); % 对称矩阵
end
end
% 能见度η(启发式因子)的初始化
Eta = 1./city_distance;
% 开始迭代
iterator = 1;
min_distance = inf; % 记录最小值
while iterator<Iterator_Max
% 1.将蚂蚁随机分配到不同的城市
city_choice=randperm(city_num);
for i = 1:city_num
ant_way(i,1)=city_choice(i);
% 禁忌表标记
available_city(i,ant_way(i,1))= 0;
end
% 多出的蚂蚁的随机分布
for i=city_num+1:ant_num
ant_way(i,1)=randperm(city_num,1);
% 禁忌表标记
available_city(i,ant_way(i,1))=0;
end
% 蚂蚁开始周游
for N=2:city_num
% 2.计算每只蚂蚁下一步去往所有城市的概率
ant_city_choice_P = zeros(ant_num,city_num);
for i=1:ant_num
for j=1:city_num
% 已经去过的城市概率为0
if available_city(i,j)==0
ant_city_choice_P(i,j)=0;
else
% 去第j个城市的概率 = 信息素浓度(目前所在城市,目标城市) * 能见度
ant_city_choice_P(i,j)=(city_pheromones(ant_way(i,N-1),j)^Alpha) * (Eta(ant_way(i,N-1),j)^Beta);
end
end
end
for i=1:ant_num
% 换算成概率
P = sum(ant_city_choice_P(i,:)); % 总概率
for j=1:city_num
ant_city_choice_P(i,j)=ant_city_choice_P(i,j)/P;
end
% 轮盘选择下一个城市
cumsumP=cumsum(ant_city_choice_P(i,:)); % 元素()之前的所有元素和为总数
select = find(cumsumP>=rand); % 轮盘根据概率选择可能的路线
% 重复判断
while available_city(i,select(1))==0
select = find(cumsumP>=rand);
end
% 信息同步
ant_way(i,N) = select(1); % 路径更新
ant_distance(i)= ant_distance(i)+city_distance(ant_way(i,N-1),select(1)); % 总路程长度更新
available_city(i,select(1)) = 0; % 禁忌表更新
end
end
% 所有的蚂蚁回到初始位置
for x=1:ant_num
ant_way(x,city_num+1) = ant_way(x,1);
ant_distance(x)= ant_distance(x)+city_distance(ant_way(x,city_num),ant_way(x,city_num+1));
end
% 周游结束
% 计算信息素的残留
city_pheromones = (1-Rho).* city_pheromones;
for i=1:ant_num
for j=1:city_num
city_pheromones(ant_way(i,j),ant_way(i,j+1))=city_pheromones(ant_way(i,j),ant_way(i,j+1))+Q/ant_distance(i);
%此次循环在路径(i,j)上的信息素增量
end
end
% 取得本次最短路径
md = min(min(ant_distance));
if min_distance> md
min_distance = md;
% 找到最短路径--略
end
% 禁忌表归为最初状态
available_city=ones(ant_num,city_num);
ant_way = zeros(ant_num,city_num+1);
ant_distance = zeros(ant_num,1);
iterator = iterator+1;
end
min_distance
步骤总结
变量的初始化
- α :表示信息素在指导蚂蚁选择路径的重要程度。
- β :表示能见度η(启发式因子)在路径选择中的重要程度。
- ρ :表示信息素挥发的效率。
- m :表示蚂蚁的数量(目标数量的1.5倍左右)
- Q :表示每只蚂蚁所含的信息素(不同的模型拥有不同的计算方法)
- t :表示最大的迭代次数
迭代的初始化
分别(使用矩阵或者变量)记录变量
- city_distance:记录每两个城市之间的距离(可以使用对称矩阵)
- city_pheromones:城市间的路径内的信息素浓度。
- ant_way:记录蚂蚁行径城市的顺序。
- ant_city_choice_P:记录每次询问时,选择每一座城市的概率。
- 初始化禁忌表
- 初始化蚂蚁总距离的记录
每一次迭代,先要将蚂蚁随机分布在城市中,最后都需要蚂蚁回到起点,所以路径需要加上终点距离起点的的距离。
补充
邻接矩阵(Adjacency Matrix)
邻接矩阵是表示顶点之间相邻关系的矩阵。
设G=(V,E)是一个图,其中V={v1,v2,…,vn}。G的邻接矩阵是一个具有下列性质的n阶方阵:
①对无向图而言,邻接矩阵一定是对称的,而且对角线一定为零,有向图则不一定如此。
②在无向图中,任一顶点 i 的度为第 i 列所有元素的和,在有向图中顶点 i 的出度为第 i 行所有元素的和,而入度为第 i 列所有元素的和。
③用邻接矩阵法表示图共需要n^2个空间,由于无向图的邻接矩阵一定具有对称关系,所以扣除对角线为零外,仅需要存储上三角形或下三角形的数据即可,因此仅需要n(n-1)/2个空间。
评论