使用蚁群算法求解TSP旅行商问题

# -*- coding: utf-8 -*-
# author: 'boliang'
# date: 2018/5/7 20:04

import numpy as np
import matplotlib.pyplot as plt

inf = float('inf')
rec_dis = inf
best_record = []

class AntColonySystem(object):
   def __init__(self, d, ant_sum, alpha, beta, ro):
       self.__d = d
       self.__alpha = alpha
       self.__beta = beta
       self.__ro = ro
       self.__ant_sum = ant_sum
       self.__city_len = len(self.__d)
   '''
   description: 改变一条边的信息素(ACS的优化方法)
   '''
   def __change_local_phe(self, pheromone, i, j, phe_init):
       alsimon = 0.1
       pheromone[i][j] = (1-alsimon) * pheromone[i][j] + alsimon*phe_init
       pheromone[j][i] = pheromone[i][j]

   '''
   descritpion: 更新全局信息素, ACS为基准(效果较优)
   '''
   def __change_global_phe(self, pheromone, min_dis, best_record):
       for i in range(self.__city_len):
           for j in range(self.__city_len):
               pheromone[i][j] = (1 - self.__ro) * pheromone[i][j]
               ind_a = best_record.index(i)
               ind_b = best_record.index(j)
               if abs(ind_a - ind_b) == 1 or abs(ind_a - ind_b) == self.__city_len - 1:
                   pheromone[i][j] += self.__ro * (1 / min_dis)

   '''
   description: 获得可选择的城市的概率与城市列表
   return:
       next_ps 所有可选择城市的概率
       next_cities 所有可选择的城市
   '''
   def __get_probilities(self, records, ant, pheromone):
       cur_c = records[ant][-1]
       next_cities = [next_c for next_c in range(self.__city_len) if
                      next_c not in records[ant] and self.__d[cur_c][next_c] != inf]
       next_cs_alpha = list(map(
           lambda next_c: (pheromone[cur_c][next_c] ** self.__alpha) * ((1 / self.__d[cur_c][next_c]) ** self.__beta),
           next_cities))
       beta = sum(next_cs_alpha)
       return list(map(lambda alpha: alpha / beta, next_cs_alpha)), next_cities

   '''
   当前的蚂蚁选择下一个城市
   return next_c 返回选择好的城市号
   '''
   def __choose_next_city(self, records, record_dis, ant, pheromone, next_ps, next_cities):
       cur_c = records[ant][-1]
       select_id = 0
       if np.random.random() < 0.7:
           tmp = list(map(lambda next_c: pheromone[cur_c][next_c]*((1/self.__d[cur_c][next_c])**self.__beta), next_cities))
           select_id = tmp.index(max(tmp))
       else:
           num = 0
           select_num = np.random.random()
           for tmp in next_ps:
               num += tmp
               if select_num <= num:
                   break
               select_id += 1

       records[ant].append(next_cities[select_id])
       record_dis[ant] += self.__d[cur_c][next_cities[select_id]]
       return next_cities[select_id]


   '''
   description: 主方法,调用所有子方法进行问题迭代求解
   '''
   def solve(self, step):
       pheromone = [[1 for i in range(self.__city_len)] for j in range(self.__city_len)]
       best_record = []
       min_dis = inf
       records = [list() for i in range(self.__ant_sum)]
       record_dis = [0 for i in range(self.__ant_sum)]
       is_init = True
       pheromone_init = 0

       records_dis = []

       while step > 0:
           for ant in range(self.__ant_sum):
               records[ant] = [np.random.randint(0, self.__city_len)]
               record_dis[ant] = 0
               while len(records[ant]) < self.__city_len:
                   # 得到当前城市
                   cur_c = records[ant][-1]
                   # 获得下一个可以选择的所有城市选择概率与可选择的城市列表
                   next_ps, next_cities = self.__get_probilities(records, ant, pheromone)
                   # 选择下一个城市,返回选择好的城市号,便于局部更新信息素
                   next_c = self.__choose_next_city(records, record_dis, ant, pheromone, next_ps, next_cities)
                   if is_init == False:
                       # 局部更新信息素
                       self.__change_local_phe(pheromone, cur_c, next_c, pheromone_init)

               # 把头尾城市接上
               record_dis[ant] += self.__d[records[ant][0]][records[ant][-1]]
               if is_init == False:
                   self.__change_local_phe(pheromone, records[ant][0], records[ant][-1], pheromone_init)

               # 更新最优路径
               if min_dis == inf or min_dis > record_dis[ant]:
                   best_record = records[ant]
                   min_dis = record_dis[ant]

           # 是否初始化
           if is_init:
               is_init = False
               pheromone_init = 1/(self.__city_len*min_dis)
               pheromone = [[pheromone_init for i in range(self.__city_len)] for j in range(self.__city_len)]
               continue

           # 全局更新信息素
           self.__change_global_phe(pheromone, min_dis, best_record)
           step -= 1
           records_dis.append(min_dis)
       plt.plot([i for i in range(len(records_dis))], records_dis)
       plt.xlabel('times')
       plt.ylabel('distance')
       plt.show()
       print('计算出的序列为:', end='')
       print(best_record)
       print('最小值为:', end='')
       print(min_dis)

if __name__ == '__main__':
   cities = [[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]]

   d = []
   for i in range(len(cities)):
       d.append([])
       for j in range(len(cities)):
           if i == j:
               d[-1].append(inf)
           else:
               d[-1].append( np.sqrt((cities[i][0]-cities[j][0])**2 + (cities[i][1]-cities[j][1])**2) )


   ant_sum = 30
   alpha = 1
   beta = 2
   ro = 0.1
   sol = AntColonySystem(d, ant_sum, alpha, beta, ro)
   sol.solve(100)

迭代图

result.png

输出结果(走的城市序列和走过的路径长度)

image.png