流程图
数据集
att48.tsp【下载链接】
关键要素
- 信息素更新:蚁周系统
代码
import random
import numpy as np
import copy
import time
import pandas as pd
import math
import matplotlib.pyplot as plt
def deal_data(city):
city = np.array(city[0][6:len(city) - 1])
city_name = city.tolist()
chu_li = []
for i in range(0, len(city_name)):
temp = city_name[i].split()
del temp[0]
for x in range(0, 2):
temp[x] = eval(temp[x])
chu_li.append(temp)
return chu_li
def display(city_location,best_route,satisfactory_solution,means):
X = []
Y = []
for i in best_route:
plt.scatter(city_location[i][0], city_location[i][1],color='b')
x = city_location[i][0]
y = city_location[i][1]
X.append(x)
Y.append(y)
X.append(city_location[best_route[0]][0])
Y.append(city_location[best_route[0]][1])
plt.plot(X,Y,'-o')
plt.title("satisfactory solution of TS:%d"%(int(min(satisfactory_solution))))
plt.show()
#绘制迭代过程图
A = [i for i in range(len(means))]#横坐标
B = satisfactory_solution[:] #纵坐标
C=means[:]
plt.xlim(0,len(means))
plt.xlabel('进化次数',fontproperties="SimSun")
plt.ylabel('路径里程',fontproperties="SimSun")
plt.title("solution of ACO changed with evolution")
plt.plot(A,B,'b-')
plt.plot(A,C,'r-')
plt.legend(["best","average"])
plt.show()
return
class ACO:
def __init__(self, m, alpha, beta, num_task, rho, Q, iter,dis):
self.m = m # 蚂蚁数量
self.alpha = alpha # 信息素重要程度因子
self.beta = beta # 期望值重要程度因子
self.num_task = num_task # 城市数量
self.pheromonetable = np.ones((self.num_task, self.num_task)) # 初始化信息素矩阵,全为1
self.candidate = np.zeros((m, num_task)).astype(int) # 候选集列表,为蚂蚁数量*任务数量
self.rho = rho # 挥发速度
self.Q = Q # 信息素常数
self.iter = iter # 最大迭代次数
self.dis=dis
def initialization(self): # 初始化蚂蚁位置
# 注:信息素初始化已在蚁群算法构造函数中完成
# 在此填入蚂蚁位置初始化代码
init_position = []
for i in range(self.m):
init_position.append(random.randint(0,self.num_task-1))
return init_position # 一个列表,代表每个蚂蚁的初始位置,[2,4,5,7],分别表示第一个蚂蚁先执行任务2,第二个蚂蚁先执行任务4
def calculate(self,paths):
diss=[]
for i in range(len(paths)):
dis_sum=0
for j in range(self.num_task-1):
dis_sum+=self.dis[paths[i][j]][paths[i][j+1]]
dis_sum+=self.dis[paths[i][-1]][paths[i][0]]
diss.append(dis_sum)
return diss
def construct_solution(self, position): # 构造路径,传入每只蚂蚁的初始位置
profit_list = [] # 每迭代一次m个蚂蚁遍历的所有解的收益
seq_list = []
plan=[]
for i in range(self.m): # 每只蚂蚁
#在此填入蚂蚁路径构造代码,路径以列表形式存在self.candidate[i]中,如,self.candidate[i] = [0,1,2,3,4,5,6,7]
#注1:全部任务的收益之和,使用 datainstantiation.profit 获取
#注2:单个任务的收益值,如任务j,使用 datainstantiation.tasklists[j][3] 获取
task_index1 = position[i] # 每只蚂蚁访问的第一个城市下标
task_path = [task_index1] # 记录每只蚂蚁访问过的城市
tabu = [task_index1] # 记录每只蚂蚁访问过的城市下标,禁忌城市下标列表
non_tabu = list(set(range(self.num_task)) - set(tabu))
for j in range(self.num_task-1): # 对余下的任务进行访问
up_proba = np.zeros(self.num_task-len(tabu)) # 初始化状态迁移概率的分子
for k in range(self.num_task-len(tabu)):
font= np.power(self.pheromonetable[task_index1][non_tabu[k]], self.alpha) #信息素重要度
behind=np.power(1/self.dis[task_index1][non_tabu[k]], self.beta) #任务重要度
up_proba[k] =font*behind
proba = up_proba/sum(up_proba) # 每条可能子路径上的状态迁移概率
while True: # 提取出下一个任务的下标
random_num = np.random.rand()
index_need = np.where(proba > random_num)[0]
if len(index_need) > 0:
task_index2 = non_tabu[index_need[0]]
break
task_path.append(task_index2)
tabu.append(task_index2)
non_tabu = list(set(range(self.num_task)) - set(tabu))
task_index1 = task_index2
self.candidate[i]=task_path[:]
for j in range(1):#迭代
#寻找上一个最优路径对应的所有领域解
path_new=[self.candidate[i]]
for p in range(1,self.num_task-1):
for q in range(i+1,self.num_task-1):
path=self.candidate[i].copy()
path[p:q+1]=list(reversed(path[p:q+1]))
path_new.append(path)
dis_new=self.calculate(path_new)
dis_best=min(dis_new)#最短距离
path_best=path_new[dis_new.index(dis_best)]#对应的最短路径方案
self.candidate[i]=path_best[:]
'''
###计算收益(距离)
profit=0
for j in range(self.num_task-1):
profit+=self.dis[self.candidate[i][j]][self.candidate[i][j+1]]
profit+=self.dis[self.candidate[i][-1]][self.candidate[i][0]]
'''
profit_list.append(dis_best)
return profit_list
def update(self, profit_list,best_route,best): # 更新信息素
changepheromonetable = np.zeros((self.num_task, self.num_task)) #存储每条路径上的信息素增量
#在此填入信息素更新的代码
#注1:profit_list存储的是每一只蚂蚁路径构造的解的收益值,即Gk,蚂蚁k的解收益值为profit_list[k]
#注2:全部任务的收益之和,使用 datainstantiation.profit 获取
for j in range(len(best_route)-1):
changepheromonetable[best_route[j]][best_route[j+1]]+=1000*1/best
changepheromonetable[best_route[-1]][best_route[0]]+=1000*1/best
for i in range(self.m):
delta=profit_list[i]*self.Q/sum(profit_list)
for j in range(len(self.candidate[i])-1):
changepheromonetable[self.candidate[i][j]][self.candidate[i][j+1]]+=delta
changepheromonetable[self.candidate[i][-1]][self.candidate[i][0]]+=delta
self.pheromonetable = (1 - self.rho) * self.pheromonetable + changepheromonetable
def main_process(self):
profit_iter = [] #存储每一代的最佳收益
profit_iter_mean=[]
for i in range(self.iter):
position = copy.deepcopy(self.initialization())
profit_list = self.construct_solution(position)
profit_iter_mean.append(sum(profit_list)/self.m)
profit_iter.append(min(profit_list)) # 获得每一代的最优值
if min(profit_list)<=min(profit_iter):
best_route=self.candidate[profit_list.index(min(profit_list))].copy()
self.update(profit_list,best_route,min(profit_iter))
# 更新最优
return profit_iter_mean,profit_iter,min(profit_iter),best_route
data = pd.read_csv('att48.tsp', encoding='utf-8-sig', header=None)
city_location=deal_data(data)
city_count=len(city_location)
dis =[[0]*city_count for i in range(city_count)] #距离矩阵
for i in range(city_count):
for j in range(city_count):
if i != j:
dis[i][j] = math.sqrt((city_location[i][0]-city_location[j][0])**2 + (city_location[i][1]-city_location[j][1])**2)
else:
dis[i][j] = 0
a=ACO(20,2,4,city_count,0.5,10,100,dis)
#注:若修改任务数量,则datainstantiation.py中读取代码的任务数量也要一同修改
starttime = time.time()
profit_iter_mean,profit_iter,best,best_route=a.main_process()
print(round(time.time()-starttime,2))
print("所求最优解为:%d" % int(best))
display(city_location,best_route,profit_iter,profit_iter_mean)
部分结果展示
数据集 | 运行时间(s) | 最短距离 | 官方距离 | |
---|---|---|---|---|
att48 | 149 | 33976 | 33533 | |
路线图 | 迭代下降图 | |||
小结
从结果可以看出,求解质量很不好。后续有时间的话,我会对其进行改进如精英蚂蚁、最大最小蚂蚁系统、蚁群系统等等,同时也可与局部搜索算法配合使用。