Python实现蚁群算法求解旅行商问题

效果

展示效果

思路

1.初始化

步骤内容
Step 1随机生成所有城市的坐标 (city_x, city_y)
Step 2计算任意两城市之间的距离 (distance) 和能见度 (eta)
Step 3用贪婪算法得出初始路径
Step 4计算得出并记录所有路径的信息素浓度(tau)

2.多次迭代

步骤内容
Step 1第 t 次迭代
Step 2第 t 次迭代, 初始化: 所有蚂蚁 (m) 的路径 (path[m][]), 出发城市 (path[m][0]),允许去的城市 (allowed[m][]) 即本次迭代中未去过的城市
Step 3第 t 次迭代, 第 c 次选择下一个城市
Step 4第 t 次迭代, 第 c 次选择下一个城市, 第 m 只蚂蚁选择下一个城市
Step 5第 t, c, m 中, 初始化:估值函数 pij[0][], 估值的概率占比 pij[1][], 比例选择时的概率点 pij[2][]
Step 6第 t, c, m 中, 获得蚂蚁 m 的允许去的城市 cho = []
Step 7第 t, c, m 中, 计算蚂蚁 m 去城市 n 的 估值函数
Step 8第 t, c, m 中, 计算蚂蚁 m 去城市 n 的 估值函数值 在所有估值函数值之和的占比
Step 9第 t, c, m 中, 计算比例选择 (轮盘赌) 时, 去城市 n 还是城市 n+1 的概率选择点
Step 10第 t, c, m 中, 模仿轮盘赌, 随机比例选择
Step 11第 t 次迭代, 所有蚂蚁回到出发城市, 形成一条首尾相连的路径
Step 12第 t 次迭代, 计算所有蚂蚁 (m) 的路径长 (mplen[m]), 途经城市 (taumnn[m][][])
Step 13第 t 次迭代, 更新路径 (tau[i][j]) 的信息素 (蒸发剩下的 + 新留下的)
Step 14第 t 次迭代, 选出所有蚂蚁 (m) 路径长度 (mplen[m]) 中的最短路径长度
Step 15第 t 次迭代, 判断是否出现比全局最短路径更短的本次迭代最短路径, 并更新

3.展示结果

步骤内容
Step 1输出 每次迭代的最短路径长度
Step 2输出 全局最短路径长度 及其 首次出现 的 迭代次数
Step 3可视化展示 所有城市 的 位置
Step 4可视化展示 全局最短路径
Step 5可视化展示 每次迭代的最短路径长度 的变化趋势

代码

  • ACO.py
import math
import random
import copy
import matplotlib.pyplot as plt

# 地图长度
L = 100
# 地图高度
H = 80
# 城市个数
N = 20
# 蚂蚁个数
M = 20
# 迭代次数
T = 300

# 地图的对角线长度
LH = int(math.sqrt(L*L + H*H))

# 信息素的加权值
alpha = 1
# 能见度的加权值
beta = 2
# 信息素的蒸发率
rho = 0.5

# 城市的横坐标
city_x = [0 for n in range(N)]
# 城市的纵坐标
city_y = [0 for n in range(N)]

# 城市i和城市j之间的距离
distance = [[0 for j in range(N)] for i in range(N)]
# 能见度, 两点之间距离的倒数, 启发信息函数
eta = [[0 for j in range(N)] for i in range(N)]

# 当前时刻, 城市i和城市j之间的道路上信息素的值
tau = [[0 for j in range(N)] for i in range(N)]

# pathlen[t] 第 t 次迭代后得出的路径长度
pathLen = []
# pathCity[t] 第 t 次迭代后得出的路径
pathCity = []

# 第 best[0] 次迭代的路径最短, 全局最短路径的编号
best = [0]
# 全局最短路径上依次经过的城市的横坐标
X = []
# 全局最短路径上依次经过的城市的纵坐标
Y = []

# 初始化
# Step 1: 随机生成所有城市的坐标 (city_x, city_y)
# Step 2: 计算任意两城市之间的距离 (distance) 和能见度 (eta)
# Step 3: 用贪婪算法得出初始路径
# Step 4: 计算得出并记录所有路径的信息素浓度(tau)

def init():
    # ------------------------------------------- Step 1
    # 遍历所有城市
    for n in range(N):
        # 随机横坐标
        x = random.randint(0, L-1)
        # 随机纵坐标
        y = random.randint(0, H-1)
        # 记录城市 n 的横坐标
        city_x[n] = x
        # 记录城市 n 的纵坐标
        city_y[n] = y
    # ------------------------------------------- Step 2
    # 从城市 i 出发
    for i in range(N):
        # 到达城市 j 
        for j in range(N):
            # 城市 i 和城市 j 之间的距离
            dij = math.pow(city_x[i] - city_x[j], 2)
            dij = dij + math.pow(city_y[i] - city_y[j], 2)
            dij = math.sqrt(dij)
            # 记录两城市之间的距离
            distance[i][j] = dij
            # 计算能见度
            # 如果 i 等于 j
            if i == j:
                # 城市到自己的能见度为 0
                eta[i][j] = 0
            else:
                # 两城市之间的能见度为两城市之间距离的倒数
                eta[i][j] = 1 / dij
    # ------------------------------------------- Step 3
    # 允许去的城市, 即未去过的城市
    # 0: 不允许, 1: 允许
    allow = [1 for n in range(N)]
    # 假设:从城市0出发
    allow[0] = 0
    # 路径
    apath = [0]
    # 当前位置
    apos = 0
    # 下一步去的城市
    acity = 0
    # 与下一个城市的距离
    away = 0
    # 总路径长度
    alen = 0
    #  第 c 次去往下一个城市
    for c in range(N-1):
        # 设置去往下一个城市的距离, 最大值
        away = LH
        # 选择去往哪一个城市
        for n in range(N):
            # 如果允许去城市 n
            if allow[n] == 1:
                # 如果去城市 n 的距离 小于 当前要去的城市的距离
                if distance[apos][n] < away:
                    # 更新要去的城市
                    acity = n
                    # 更新要去的城市的距离
                    away = distance[apos][n]
        # 更新所在的位置
        apos = acity
        # 更新路径
        apath.append(apos)
        # 更新总路径长度
        alen = alen + away
        # 更新允许去的城市
        allow[apos] = 0
    # 回到出发点
    apath.append(0)
    # 更新总路径长度
    alen = alen + distance[apos][0]
    # 添加初始路径长度
    pathLen.append(alen)
    # 添加初始路径经过的城市
    pathCity.append(copy.deepcopy(apath))
    # ------------------------------------------- Step 4
    # 获得信息素的初始浓度值
    tau0 = M / alen
    # 设置所有城市之间路径的信息素浓度
    for i in range(N):
        for j in range(N):
            tau[i][j] = tau0
    # 城市到本身无路径, 信息素浓度为 0
    for n in range(N):
        tau[n][n] = 0

# 多次迭代
# Step  1: 第 t 次迭代
# Step  2: 第 t 次迭代, 初始化: 所有蚂蚁 (m) 的路径 (path[m][]), 出发城市 (path[m][0]),
#                              允许去的城市 (allowed[m][]) 即本次迭代中未去过的城市
# Step  3: 第 t 次迭代, 第 c 次选择下一个城市
# Step  4: 第 t 次迭代, 第 c 次选择下一个城市, 第 m 只蚂蚁选择下一个城市
# Step  5: 第 t, c, m 中, 初始化:估值函数          pij[0][],
#                               估值的概率占比     pij[1][],
#                               比例选择时的概率点 pij[2][]
# Step  6: 第 t, c, m 中, 获得蚂蚁 m 的允许去的城市 cho = []
# Step  7: 第 t, c, m 中, 计算蚂蚁 m 去城市 n 的 估值函数
# Step  8: 第 t, c, m 中, 计算蚂蚁 m 去城市 n 的 估值函数值 在所有估值函数值之和的占比
# Step  9: 第 t, c, m 中, 计算比例选择 (轮盘赌) 时, 去城市 n 还是城市 n+1 的概率选择点
# Step 10: 第 t, c, m 中, 模仿轮盘赌, 随机比例选择
# Step 11: 第 t 次迭代, 所有蚂蚁回到出发城市, 形成一条首尾相连的路径
# Step 12: 第 t 次迭代, 计算所有蚂蚁 (m) 的路径长度 (mplen[m]), 途经城市 (taumnn[m][][])
# Step 13: 第 t 次迭代, 更新路径 (tau[i][j]) 的信息素 (蒸发剩下的 + 新留下的)
# Step 14: 第 t 次迭代, 选出所有蚂蚁 (m) 路径长度 (mplen[m]) 中的最短路径长度
# Step 15: 第 t 次迭代, 判断是否出现比全局最短路径更短的本次迭代最短路径, 并更新

def iteration():
    # ------------------------------------------- Step 1
    # 第 t 次迭代
    for t in range(1, T+1):
        # --------------------------------------- Step 2
        # 所有蚂蚁的路径
        path = [[] for m in range(M)]
        # 蚂蚁 m 从 城市 m 出发
        for m in range(M):
            path[m].append(m)
        # 蚂蚁允许去的城市
        allowed = [[0 if i == j else 1 for j in range(N)]for i in range(N)]
        # 第 c 次去往下一个城市, 除了出发城市, 有 N-1 个城市
        # --------------------------------------- Step 3
        for c in range(N-1):
            # ----------------------------------- Step 4
            # 第 m 只蚂蚁选择下一个城市
            for m in range(M):
                # ------------------------------- Step 5
                # 估值函数, 估值的概率占比, 比例选择时的概率点
                pij = [[0 for j in range(N)] for i in range(3)]
                # 去往的下一个城市的编号
                city = 0
                # 在第 c 次选择城市时, 可选城市的编号
                # ------------------------------- Step 6
                cho = []
                # 判断城市 n 是否可选
                for n in range(N):
                    # 如果可选
                    if allowed[m][n] == 1:
                        # 添加入 cho
                        cho.append(n)
                # ------------------------------- step 7
                # 遍历所有可去城市
                for n in cho:
                    # 蚂蚁 m 所处的当前城市
                    x = path[m][-1]
                    # 蚂蚁 m 下一步去从城市 n 的概率
                    pij[0][n] = math.pow(tau[x][n], alpha)
                    pij[0][n] = pij[0][n] * math.pow(eta[x][n], beta)
                # -------------------------------step 8
                # 求和
                p1 = sum(pij[0])
                # 归一化
                for n in cho:
                    # 蚂蚁 m 去从城市 n 的概率 占 所有概率之和的比例
                    pij[1][n] = pij[0][n] / p1
                # ------------------------------- Step 9
                # 比例选择法(轮盘赌法)的第一个概率点
                p2 = 0
                # 遍历所有可去城市
                for n in cho:
                    # 获得所有概率点
                    pij[2][n] = p2 + pij[1][n]
                    p2 = pij[2][n]
                # ------------------------------- Step 10
                # 模仿轮盘, 随机选择
                rand = random.random()
                # 遍历所有可去城市
                for n in cho:
                    # 如果概率点落在去城市 n 的扇面内
                    if pij[2][n] > rand:
                        # 则去城市 n
                        city = n
                        # 结束遍历
                        break
                # 更新路径
                path[m].append(city)
                # 更新允许去的城市
                allowed[m][city] = 0
        # --------------------------------------- Step 11
        # 回到出发城市
        for m in range(M):
            # 添加路径
            path[m].append(m)
        # --------------------------------------- Step 12
        # 所有蚂蚁走完所有城市的路径长度
        mplen = []
        # 蚂蚁 m 是否经过城市 i 到城市 j 的路径, 留下信息素
        taumnn = []
        # 遍历所有蚂蚁
        for m in range(M):
            # 初始设置: 蚂蚁 m 没有经过城市 i 到城市 j 的路径
            taunn = [[0 for i in range(N)] for j in range(N)]
            # 总路径长度为 0
            plen = 0
            # 遍历蚂蚁 m 经过的城市
            for p in range(N):
                # 出发城市
                x = path[m][p]
                # 到达城市
                y = path[m][p+1]
                # 更新路径长度
                plen = plen + distance[x][y]
                # 在城市 x 到城市 y 的路径上留下信息素
                taunn[x][y] = 1
            # 更新所有蚂蚁的路径总长度的列表
            mplen.append(plen)
            # 更新所有蚂蚁留下信息素的路径列表
            taumnn.append(copy.deepcopy(taunn))
        # --------------------------------------- Step 13
        # 城市 i 出发
        for i in range(N):
            # 到达城市 j
            for j in range(N):
                # 蚂蚁留下的信息素
                taumij = 0
                # 遍历所有蚂蚁留下信息素的路径
                for m in range(M):
                    # 如果蚂蚁 m 在城市 i 到城市 j 的路径上留下信息素
                    if taumnn[m][i][j] == 1:
                        # 更新该段路径留下的的信息素之和
                        taumij = taumij + 1 / mplen[m]
                # 更新该路径的信息素(蒸发剩下的 + 新留下的)
                tau[i][j] = (1-rho) * tau[i][j] + taumij
        # --------------------------------------- Step 14
        # 路径总长度的最大值 小于 对角线长度的城市个数倍
        pathlent = LH * N
        # 蚂蚁 ant 在本次迭代中走的路径最短
        ant = 0
        # 遍历所有蚂蚁
        for m in range(M):
            # 如果蚂蚁 m 走的路径长度 小于 本次迭代的最短路径
            if mplen[m] < pathlent:
                # 更新最短路径
                pathlent = mplen[m]
                # 更新蚂蚁编号
                ant = m
        # 添加本次迭代的最短路径
        pathLen.append(pathlent)
        # 添加本次迭代的最短路径经过的城市
        pathCity.append(copy.deepcopy(path[ant]))
        # --------------------------------------- Step 15
        # 判断是否出现比全局最短路径更短的本次迭代最短路径
        if pathlent < pathLen[best[0]]:
            # 更新全局最短路径的编号
            best[0] = len(pathLen)
        

# 展示结果
# Step 1: 输出 每次迭代的最短路径长度
# Step 2: 输出 全局最短路径长度 及其 首次出现 的 迭代次数
# Step 3: 可视化展示 所有城市 的 位置
# Step 4: 可视化展示 全局最短路径
# Step 5: 可视化展示 每次迭代的最短路径长度 的变化趋势

def showResult():
    # ------------------------------------------- Step 1
    # 遍历所有的迭代结果
    for t in range(T+1):
        # 输出 t 次迭代的最短路径长度
        print("{0:>3} {1:>16}".format(t, pathLen[t]))
    # ------------------------------------------- Step 2
    # 输出全局最短路径及其长度, 首次出现的迭代次数
    print("\n全局最短路径: ", pathCity[best[0]], " 长度: ", pathLen[best[0]])
    print("首次出现在第 ", best[0], "次迭代")
    # ------------------------------------------- Step 3
    # 画布
    plt.figure()
    # 子图 1
    plt.subplot(2, 1, 1)
    # 散点图展示所有城市的位置
    plt.scatter(city_x, city_y, color='b')
    # 遍历所有城市
    for n in range(N):
        # 城市 n 的坐标信息
        msg = "({},{})".format(city_x[n], city_y[n])
        # 需标注的城市的坐标
        x = city_x[n]
        y = city_y[n]
        # 文本标注的位置
        xt = x + 0.5
        yt = y + 0.5
        # 标注
        plt.annotate(msg, xy=(x,y), xytext=(xt,yt))
    # ------------------------------------------- Step 4
    # 遍历全局最短路径经过的城市
    for p in pathCity[best[0]]:
        # 横坐标
        X.append(city_x[p])
        # 纵坐标
        Y.append(city_y[p])
    # 折线图展示: 全局最短路径
    plt.plot(X, Y, color='c')
    # x 轴标签
    plt.xlabel("city_x")
    # y 轴标签
    plt.ylabel("city_y")
    # 标题
    plt.title("Global shortest path")
    # 添加网格线
    plt.grid()
    # ------------------------------------------- Step 5
    # 子图 2
    plt.subplot(2, 1, 2)
    # 折线图展示: 每次迭代路径最短长度的变化趋势
    plt.plot([t for t in range(T+1)], pathLen)
    # x 轴标签
    plt.xlabel("T iteration")
    # y 轴标签
    plt.ylabel("The shortest length")
    # 标题
    plt.title("Changing trend")
    # 展示输出
    plt.show()

# ******************** 程序开始 *********************
print("START !")
# 初始化
init()
# 多次迭代
iteration()
# 展示结果
showResult()
# ******************** 程序结束 *********************
print("END !")

留言

提出不足之处,后续改进,感谢支持

如果有你要的内容,给个赞吧!

  • 10
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
蚁群算法是一种基于蚂蚁寻找食物的行为模式的启发式算法,可以用来解决TSP问题。在Python实现蚁群算法求解TSP问题的步骤如下: 1. 定义TSP问题的数据结构,包括城市坐标、城市之间距离矩阵等。 2. 初始化蚂蚁群,包括蚂蚁数量、信息素矩阵、城市访问状态等。 3. 根据信息素和距离计算城市之间的转移概率,选择下一个要访问的城市。 4. 更新蚂蚁的路径、信息素矩阵等。 5. 重复步骤3和4,直到所有蚂蚁都完成了一次遍历。 6. 更新全局最优解。 7. 根据全局最优解更新信息素矩阵。 8. 重复步骤3到7,直到满足停止条件。 下面是一个简单的Python代码实现,仅供参考: ```python import random class Ant: def __init__(self, city_num): self.city_num = city_num self.visited = [False] * city_num self.path = [0] * city_num self.path[0] = random.randint(0, city_num - 1) self.visited[self.path[0]] = True self.length = 0.0 def select_next(self, distance, pheromone, alpha, beta): last_city = self.path[self.length - 1] prob = [0.0] * self.city_num total_prob = 0.0 for i in range(self.city_num): if self.visited[i]: continue prob[i] = pow(pheromone[last_city][i], alpha) * pow(1.0 / distance[last_city][i], beta) total_prob += prob[i] if total_prob == 0.0: return random.randint(0, self.city_num - 1) r = random.uniform(0, total_prob) for i in range(self.city_num): if not self.visited[i]: r -= prob[i] if r < 0.0: return i return -1 def update_path(self, city): self.path[self.length] = city self.visited[city] = True self.length += 1 def calc_length(self, distance): self.length = 0.0 for i in range(self.city_num): self.length += distance[self.path[i - 1]][self.path[i]] def clear(self): self.visited = [False] * self.city_num self.path = [0] * self.city_num self.path[0] = random.randint(0, self.city_num - 1) self.visited[self.path[0]] = True self.length = 0.0 class ACO_TSP: def __init__(self, city_num, ant_num, max_iter, rho, alpha, beta, distance): self.city_num = city_num self.ant_num = ant_num self.max_iter = max_iter self.rho = rho self.alpha = alpha self.beta = beta self.distance = distance self.pheromone = [[1.0 / (city_num * city_num) for j in range(city_num)] for i in range(city_num)] self.ants = [Ant(city_num) for i in range(ant_num)] self.best_ant = Ant(city_num) self.best_ant.length = 1e9 def update_pheromone(self): for i in range(self.city_num): for j in range(self.city_num): self.pheromone[i][j] *= (1.0 - self.rho) for ant in self.ants: for i in range(self.city_num): self.pheromone[ant.path[i - 1]][ant.path[i]] += 1.0 / ant.length def solve(self): for iter in range(self.max_iter): for ant in self.ants: ant.clear() for i in range(self.city_num - 1): city = ant.select_next(self.distance, self.pheromone, self.alpha, self.beta) ant.update_path(city) ant.calc_length(self.distance) if ant.length < self.best_ant.length: self.best_ant = ant self.update_pheromone() return self.best_ant.path, self.best_ant.length ``` 其中,`city_num`表示城市数量,`ant_num`表示蚂蚁数量,`max_iter`表示最大迭代次数,`rho`表示信息素挥发系数,`alpha`和`beta`分别表示信息素和距离的重要程度,`distance`表示城市之间的距离矩阵。`Ant`类表示一只蚂蚁,包含选择下一个要访问的城市、更新路径等方法。`ACO_TSP`类表示蚁群算法求解TSP问题的主要逻辑,包括初始化、更新信息素、求解等方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值