旅行商问题的蚁群算法

文章介绍了旅行商问题的起源及其在现实中的应用,如航线安排和物流配送。由于问题的复杂性,它属于NP困难问题。为了解决这个问题,文章提到了蚁群算法这一仿生优化方法,并提供了一个用Python实现的标准蚁群算法程序,展示了如何初始化和更新信息素矩阵,以及蚂蚁选择路径的逻辑。
摘要由CSDN通过智能技术生成

旅行商问题是一个经典的组合优化问题,它的提出和研究有着悠久的历史。最早的描述是1759年欧拉研究的骑士环游问题,即对于国际象棋棋盘中的64个方格,走访64个方格一次且仅一次,并且最终返回到起始点。1954年,Dantzig等人用线性规划的方法取得了旅行商问题的历史性的突破——解决了美国49个城市的巡回问题。

旅行商问题在现实生活中有很多应用场景,如飞机航线的安排、公路网络的建设、物流货物配送等。由于这些场景中涉及到大量城市或地点之间的距离或成本,如果要求出精确解需要枚举所有可能路径并比较其总和,这样计算量会随着城市数量呈指数级增长,非常耗时甚至无法完成,它是组合优化中的NP困难问题。因此,人们寻找各种近似算法或启发式算法来求解该问题。蚁群算法(Ant Colony Algorithm)是一种仿生算法,最开始由意大利学者通过观察蚂蚁觅食,得到的2。它的基本思想是模拟蚂蚁在寻找食物时所留下的信息素,并根据信息素浓度来指导自己行走方向的过程。

下面的程序按照标准的蚂蚁算法编写,共人工智能专业的同学们对照、参考。

import numpy as np
import matplotlib.pyplot as plt

cities = [] # 为代码简单,使用全局变量。cities 储存城市的列表
city_dis_matrix = [] # 储存城市之间距离的二维列表
tau_mat = [] # 储存信息素的列表。不必单独定义启发因子矩阵

def read_cities(dat_file): # 读入dat_file 中的数据,即数据本地化。将数据读成列表格式,来自教材程序。最好是直接读成np.array格式。
    with open(dat_file, 'rt') as f:
        temp_list = list(line.split() for line in f)
    return [list(map(int,k)) for k in temp_list]   # 测试成功读入,读成二维列表。形式如[[1,2],[3,4],...] 还有方法 np.genfromtxt,没有试

def dist_between(i,j): # 两个城市距离,城市形式是i ,j 是[2,3],这样的列表。不是,i, j 就是 2,3这样的整数,指的是cities的第 i j个城市
    # 对城市的指称不统一,以后说那个城市,就是指相对于城市列表 cities 的第几个,所以
    t_vector = np.array(cities[i]) - np.array(cities[j])
    return np.linalg.norm(t_vector)


def init_dis_hpero_mat(): # list_city的格式是:[[1,2],[5,2],...],初始化城市之间的距离和信息素矩阵
    cities_num = len(cities)                   # 1. 获取城市个数
    city_dist_mat = np.ones([cities_num, cities_num]) # 先生成一个1 阵,然后修改成距离,构建城市距离矩阵
    tau_mat = np.ones([cities_num, cities_num]) # 初始化信息素矩阵,全是 1。
    for i in range(cities_num):
        for j in range(i+1, cities_num):
            city_dist_mat[i][j] = city_dist_mat[j][i] = dist_between(i,j)
            tau_mat[i][j] = tau_mat[j][i] = 1/dist_between(i,j)**2 # 初始化信息矩阵,距离倒数           
    # tau_mat = np.ones([cities_num, cities_num])  # 初始化信息素矩阵,全是 1。
    return city_dist_mat, tau_mat

class ant():
    def __init__(self,startcity): #初始化蚂蚁,主要是指定蚂蚁的起始位置 startpint 的格式是 1, 2 这样的整数,是指cities的第 1,2个城市
        self.start = startcity # 蚂蚁出发的城市,是整数
        self.current = startcity # 蚂蚁移动,当前所在的城市
        self.allowed = [] # 蚂蚁可运行访问的城市 
        self.path = [startcity] # 记录蚂蚁走过的路线 path 是 [3,1,4]这样的列表,表示从走过第3,1,4个城市
        self.length = 0 # 记录蚂蚁走过的路长。
        
    def reset(self):
        self.allowed = [i for i in range(len(cities))] # 蚂蚁周游一圈后需要重置allowed,路程和路径都要归 0 和空
        self.length = 0
        self.path = []

    def deal_chosed_city(self,chosed_city): # 选择一个城市后,要做的处理。选择城市即移动到选定的城市。chosed_city是整数
        self.length += dist_between(self.current,chosed_city) # 累加这段路长。 
        self.path.append(chosed_city) # 并将其放入蚂蚁走过的路径。
        self.current = chosed_city # 蚂蚁的当前城市
        return None   

    def choose_city(self,alp = 1,beta = 3): # 在当前城市current,按概率选择下一个城市。要访问距离矩阵和信息矩阵
        self.allowed.remove(self.current)
        s = 0
        list_of_j = [] # 记录城市
        list_of_s = [] # 记录,和那个公式有关,但是没有单位化。
        for j in self.allowed:
            s = s + 10**8*tau_mat[self.current][j]**alp*city_dis_matrix[self.current][j]**(-beta) # 一段段计算公式的那个数,没有分母。
            list_of_s.append(s)
            list_of_j.append(j)
        point = np.random.uniform(0, list_of_s[-1]) # 生成一个随机数
        k = 0
        while k < len(list_of_j): # 判断随机数落在那个范围,就选哪个 j。
            if point <= list_of_s[k]:
                return list_of_j[k]
            else:
                k += 1
    
    def update_tau_mat(self,pre,cur): 
        # 此处更新信息素,蚂蚁走完从 pre 到 cur 的路径,更新路径信息素。
        delta = 1/city_dis_matrix[pre][cur] # 此处相当 Q = 1。
        return delta

# 主程序 记住城市用 i 整数表示,表示的是cities的第 i 个城市。
if __name__ == "__main__":
    print('begin!')
    ant_num = 100 # 蚂蚁数
    allants = [] # 记录全部生成的蚂蚁的列表
    Max_inter = 1000 #5 # 周游圈数。
    rho = 0.1 # 信息素蒸发率
    filename = r'city_coordinates.dat' 
    cities = read_cities(filename) # 读入城市数据,存在列表cities中。
    city_dis_matrix, tau_mat = init_dis_hpero_mat() # 根据城市列表初始化 距离矩阵,信息素矩阵
    city_num = len(cities)
    min_length = float('inf')
    min_path = []

    for k in range(ant_num): # 实例化蚂蚁,并随机放在各个城市。
        random_city = np.random.randint(0,city_num) # 随机指定一个城市
        the_ant = ant(random_city) # 初始化蚂蚁,把蚂蚁放在这个城市
        the_ant.reset() # 设置每个蚂蚁的可允许访问的城市。
        allants.append(the_ant) # 示例化的蚂蚁全部放到列表 allants 中
    
    i_th = 0 # 第一层循环的初始化
    while i_th < Max_inter: # 蚂蚁周游圈数的循环
        city_th = 0 # 第二层循环的初始化
        while city_th <= city_num - 1: #遍历全部城市的循环
            delta_mat = np.zeros([city_num,city_num]) # 第三层循环初始化,delta_mat 记录蚂蚁本次移动信息素增量的矩阵,每走一个城市需要置 0。
            for i_ant in allants:
                if city_th == city_num - 1: # 到了最后一个城市,需要回到起点。周游一圈的事情在这里处理。
                    nextcity = i_ant.start # 直接选择初始城市为下一个城市。
                    delta_mat[currentcity][nextcity] = delta_mat[currentcity][nextcity] + i_ant.update_tau_mat(currentcity, nextcity)
                    delta_mat[nextcity][currentcity] = delta_mat[nextcity][currentcity] + i_ant.update_tau_mat(currentcity, nextcity)  
                    i_ant.deal_chosed_city(nextcity) # 把next_city设为蚂蚁的当前城市,即蚂蚁回到原来的出发城市。
                    if i_ant.length < min_length:
                        min_length = i_ant.length
                        min_path = i_ant.path
                    i_ant.reset()
                else:
                    currentcity = i_ant.current # 蚂蚁当前位置在蚂蚁内部的变量记录,取出
                    nextcity = i_ant.choose_city(currentcity) # 在当前位置选择下一个城市,
                    delta_mat[currentcity][nextcity] = delta_mat[currentcity][nextcity] + i_ant.update_tau_mat(currentcity, nextcity)
                    delta_mat[nextcity][currentcity] = delta_mat[nextcity][currentcity] + i_ant.update_tau_mat(currentcity, nextcity) 
                    # 选中的next_city的意思就是移动到next_city,计算路径信息素的增量并累加在delta_mat 中
                    i_ant.deal_chosed_city(nextcity) # 把next_city设为蚂蚁的当前城市。
            tau_mat = (1-rho)*tau_mat + delta_mat # 用累加的增量矩阵更新信息素矩阵。这里是矩阵运算,验证可行否?
            city_th += 1
        i_th += 1
    print('最短路径是{},最短路径长度是{}'.format(min_path,min_length))

    # 图示结果
    xc, yc = [x[0] for x in cities], [x[1] for x in cities]
    plt.plot(xc, yc, '.', color='blue')
    geo_path = [cities[k] for k in min_path] # 路径的坐标
    pxc,pyc = [x[0] for x in geo_path], [x[1] for x in geo_path]
    plt.plot(pxc, pyc, color='red')    
    plt.show()

程序应用的数据文件如下,将其复制,在一个文本编辑器中粘贴,并保存为 city_coordinates.dat。

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

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值