蚁群算法(Ant Colony Algorithm)最后于1992年由意大利学者M.Dorigo等人提出,它是一种模仿自然界中实在蚁群觅食行为的仿生优化算法。钻研发现:每只蚂蚁觅食时在走过的路线上会留下一种称为信息素的物质,蚂蚁之间靠感知这种物质的浓度进行信息传递。蚂蚁在抉择门路时总是偏向于朝信息索浓度高的方向挪动,而间隔短的门路上走过的蚂蚁多,留下的信息素也多,后续蚂蚁抉择它的概率也会越大;其余门路上的信息素会随着工夫的推移一直挥发,这样就造成了一种正反馈机制,最初整个蚁群汇集到最短门路上。
人工蚁群算法模仿了这一过程。每只蚂蚁在解空间独立地搜寻可行解,解越好留下的信息素越多,随着算法推动,较优解门路上的信息素增多,抉择它的蚂蚁也随之增多,最终收敛到最优或近似最优的解上。
一、算法原理
蚂蚁零碎是最早的蚁群零碎,它采纳 \(\tau_{ij}(t)\) 来模拟t时刻门路 \(i\) 到 \(j\) 下面的信息残留量,即信息素浓度。相似于蚂蚁觅食过程,每条门路下面的信息素会挥发,如果有蚂蚁通过的时候,信息素的浓度会相应减少。因而,蚂蚁零碎中的信息素浓度的更新公式为:
$$\tau_{ij}(t+n)=\rho·\tau_{ij}(t)+\Delta\tau_{ij}$$
式中,\(\rho\) 是一个 \(0\) 到 \(1\) 的数字,\((1-\rho)\) 为挥发因子。另外,\(\Delta\tau_{ij}\) 示意一次旅行(遍历完所有城市)后,所有门路\(i\) 到 \(j\)的蚂蚁留下的信息素总量,即:
$$\Delta\tau_{ij}=\sum_{k=1}^m\Delta\tau_{ij}^kr$$
式中,\(\Delta\tau_{ij}^k\) 示意第k只蚂蚁在门路 \(i\) 到 \(j\) 下面留下的信息素量。如果第 \(k\) 只蚂蚁通过门路 \(i\) 到 \(j\) ,则:
$$\Delta\tau_{ij}^k=\frac{Q}{L_kr}$$
式中,\(Q\) 为一个常数,\(L_k\) 为蚂蚁曾经走过门路的总长度。否则,第 \(k\) 只蚂蚁在 \(i\) 到 \(j\) 下面留下的信息素量为 \(0\)。
一般来说有了信息素浓度的更新公式,就能够间接给出蚂蚁对每条门路的抉择概率了。然而,为了更好的利用TSP问题本身的性质,M.Dorigo等引入了一个启发项:\(\eta=\frac{1}{d_{ij}}\) 。通过联合信息素浓度和启发因子,能够失去蚂蚁抉择门路 \(i\) 到 \(j\) 的概率为:
$$p_{ij}^{k}(t)=\left\{\begin{array}{rcl}\frac{[\tau_{ij}(t)]^\alpha·[\eta_{ij}]^{\beta}}{\sum_{k= \in allowed \: [\tau_{ik}(t)]^\alpha·[\eta_{ik}]^{\beta} } }, && {j \in allowed_k}\\ 0, & & {else}\end{array} \right.$$
式中,\(\alpha\) 和 \(\beta\) 是调节因子,用于调节 \(\tau_{ij}(t)\) 和 \(\eta_{ij}\) 之间的作用。此外 \(allowed_k\) 示意蚂蚁\(k\) 还没有走过的门路(用禁忌表存储曾经走过的门路),通过这种存储能够保障所有解的逻辑可行。如果门路 \(i\) 到 \(j\) 上的信息浓度越大 \(\tau_{ij}(t)\) 的值就越大,该门路被抉择的概率就越大;同样,如果该门路长度越短,则 \(\eta=\frac{1}{d_{ij}}\) 越大,该门路被抉择的概率也越大。
求解TSP问题的蚁群算法中的人工蚂蚁具备以下特点:
1)他们概率性地抉择下一条门路,该概率与门路长度和门路上的信息素浓度无关;
2)为了保障解的逻辑可行,蚂蚁不容许抉择曾经走过的门路(通过禁忌表实现);
3)蚂蚁走过一条门路时会在该门路下面分泌一种叫做信息素的物质。
二、代码实现
import randomimport numpy as npimport math#==========================================#对称矩阵,计算任意两个城市之间的间隔def distance_p2p_mat(): dis_mat=[] for i in range(num_city): dis_mat_each=[] for j in range(num_city): dis=math.sqrt(pow(location[i][0]-location[j][0],2)+pow(location[i][1]-location[j][1],2)) dis_mat_each.append(dis) dis_mat.append(dis_mat_each) # print(dis_mat) return dis_mat#计算所有寻找到的门路对应的间隔def cal_newpath(dis_mat,path_new): dis_list=[] for each in path_new: dis=0 for j in range(num_city-1): dis=dis_mat[each[j]][each[j+1]]+dis dis=dis_mat[each[num_city-1]][each[0]]+dis#回家 dis_list.append(dis) return dis_list#==========================================location=np.loadtxt('./city_location.txt')num_ant=200 #蚂蚁个数num_city=30 #城市个数alpha=1 #信息素影响因子beta=5 #冀望影响因子info=0.1 #信息素的挥发率Q=1 #常数count_iter = 0 #迭代计数器iter_max = 30 #迭代次数dis_list=distance_p2p_mat() #计算任意两个城市间的间隔dis_mat=np.array(dis_list) #将list转化为矩阵e_mat_init=1.0/(dis_mat+np.diag([10000]*num_city)) #冀望矩阵。加对角阵是原矩阵对角线为0,而除数不能是0,所以先用一个比拟大的数垫一下diag=np.diag([1.0/10000]*num_city) #上一步生成的num_city*num_city维的对角线为1000的对角矩阵e_mat=e_mat_init-diag #曾经做过除法了,所以让对角线元素还原,再减去那个对角矩阵pheromone_mat=np.ones((num_city,num_city)) #信息浓度矩阵。初始化每条边的信息素浓度,全1矩阵path_mat=np.zeros((num_ant,num_city)).astype(int) #蚂蚁的门路矩阵。初始化每只蚂蚁门路,都从0城市登程.(如果不加数据转化,则默认生成的是float类型的0,即0.0)while count_iter < iter_max: #最外层迭代 for ant in range(num_ant): #对每一只蚂蚁进行剖析 visit=0 #都从0城市登程 unvisit_list=list(range(1,30)) #未拜访的城市。这个语句生成一个[1..29]的数组。再加上对立的出发点0,共30个城市 for j in range(1,num_city): #j代表第ant个蚂蚁的第j步 trans_list=[] trans=0 for k in range(len(unvisit_list)): #第ant个蚂蚁的第j步取哪个城市 trans +=np.power(pheromone_mat[visit][unvisit_list[k]],alpha)*np.power(e_mat[visit][unvisit_list[k]],beta) #计算第ant个蚂蚁由visit地位向k地位走的概率。这里要留神:间接累加 trans_list.append(trans) #将 每一步 累加的后果保留到一个数组中 #轮盘法抉择下一个城市 rand=random.uniform(0,trans)#产生随机数 for t in range(len(trans_list)): if(rand <= trans_list[t]): #因为之前就曾经累加了,trans_list[t]肯定是一个递增数组,所以能够间接与trans_list[t]相比拟 visit_next=unvisit_list[t] #抉择下标为t的城市作为这个蚂蚁下一步的方向 break path_mat[ant,j]=visit_next #装填这只蚂蚁的门路矩阵 unvisit_list.remove(visit_next) #在未走的城市列表中删去这个结点。这个操作,就会使unvisit_list这个数组变成断断续续的 visit=visit_next #更新这只蚂蚁的以后地位 #所有蚂蚁的门路表填满之后,算每只蚂蚁的总间隔 dis_allant_list=cal_newpath(dis_mat,path_mat) #选取领有最短门路的蚂蚁的门路 # 留神:这里其实能够不必写成if-else构造,但那样对于每一次迭代都须要一次min和max计算,而有时候后一次迭代后果并不一定比前一次迭代后果更优,就会造成冗余计算。 # 应用if-else构造后,每一次迭代只是一个判断,并不需要每次都进行min和max计算 if count_iter == 0: dis_new=min(dis_allant_list) path_new=path_mat[dis_allant_list.index(dis_new)].copy() else: if min(dis_allant_list) < dis_new: dis_new=min(dis_allant_list) path_new=path_mat[dis_allant_list.index(dis_new)].copy() # 为了防止残留信息素过多而吞没启发式信息,所以要及时的更新信息素矩阵 pheromone_change=np.zeros((num_city,num_city)) for i in range(num_ant): for j in range(num_city-1): pheromone_change[path_mat[i,j]][path_mat[i,j+1]] += Q/dis_mat[path_mat[i,j]][path_mat[i,j+1]] pheromone_change[path_mat[i,num_city-1]][path_mat[i,0]] += Q/dis_mat[path_mat[i,num_city-1]][path_mat[i,0]] #最初一个结点到终点 pheromone_mat=(1-info)*pheromone_mat + pheromone_change count_iter += 1 #迭代计数+1,进入下一次迭代 print('最短距离:',dis_new)print('最短门路:',path_new)
city_location.txt
内容示意城市的横纵坐标,如下所示:
41 9437 8454 6725 627 642 9968 5871 4454 6283 6964 6018 5422 6083 4691 3825 3824 4258 6971 7174 7887 7618 4013 4082 762 3258 3545 2141 2644 354 50