文章目录

  • 效果
  • 思路
  • 1.初始化
  • 2.多次迭代
  • 3.展示结果
  • 代码
  • 留言


效果

蚁群算法求解最短路径python代码 蚁群算法 python_ci

思路

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 !")