• python 实现蚁群算法(simpy带绘图)


    这里使用了蚁群算法求解了旅行商问题,同时结合了simpy来绘图

    选择下一个食物的函数为:

    probability[i] = pheromone[self.now][self.not_to_foods[i]] ** pheromone_w + (
            1 / distance[self.now][self.not_to_foods[i]]) ** distance_w

    该条路概率权重=该点信息素^信息素权重*(1/路径长)^路径权重/总和,这里后面会用random.choices就不用除了

    信息素更新为:

    new_pheromone[self.route[i]][self.route[i + 1]] += pheromone_Q / self.sum_len
    pheromone = np.add((1 - volatilize) * pheromone, new_pheromone)

    总信息素会挥发volatilize比例,然后所有蚂蚁路径每一段加上固定释放的信息量/路径总长度

    话不多说,都在代码注释里了

    1. import random
    2. import simpy
    3. import matplotlib.pyplot as plt
    4. import numpy as np
    5. # 在这里我们尝试用蚁群算法求解旅行商问题,简而言之,就是寻找一条链接所有点的最短路径最后要回到初始点
    6. # 我想绘制一下每只蚂蚁的行走过程,所以使用上次的simpy包来模拟一下,
    7. # 这个simpy包主要是为了更好的可视化模拟训练过程,你也可以给每只蚂蚁单独线程代替,
    8. # 或者干脆不用管过程,只用一轮一轮的上蚂蚁,在信息素部分做点处理,光看结果也可以。总之大多要比simpy快
    9. class Ant(object): # 蚂蚁类
    10. # 说一下训练过程,有很多只蚂蚁,从随机位置出发,
    11. # 每次遵从信息素的指引,选择下一个城市,直到没有可选城市,回到初始位置然后根据路径总长度在路径上散播信息素
    12. def __init__(self): # 蚂蚁构造函数
    13. # 一只蚂蚁,只需要保存走过的食物就可以,
    14. # 这里我们为了以后整信息素时不用遍历,顺便保存一下走过的长度
    15. self.route = [] # 到达食物路径,一个例子是:[2,1,3,2]
    16. self.not_to_foods = [] # 未到达的食物点
    17. self.sum_len = 0 # 走过路径总长度
    18. self.now = 0 # 当前所在位置
    19. self.next_food = 0 # 下一个所在位置
    20. self.Initial_Position(random.randint(0,foods_n-1)) # 设定初始位置
    21. # 绘图参数
    22. self.now_position = [0, 0] # 起始到终点的向量
    23. self.proportion = env.now # 走的时间,画图就是找到起始食物点,然后加上(起始和终点的向量乘以(当前时间-出发时间)/路长)就是蚂蚁位置,
    24. def Initial_Position(self, n): # 选择初始位置'
    25. self.now = n # 当前所在的食物
    26. self.next_food = n # 下一个要到的食物
    27. self.route = []
    28. self.not_to_foods = [i for i in range(foods_n)] # 未到达的食物点
    29. self.sum_len = 0
    30. self.route.append(n) # 添加到路径
    31. self.not_to_foods.remove(n) # 初始点不会在未到的食物点
    32. def Set_Now(self, next_food): # 到下一个点
    33. self.next_food = next_food # 下一个要到的食物
    34. self.route.append(next_food) # 添加到路径
    35. self.sum_len += distance[self.now][next_food] # 总路径增加
    36. if next_food in self.not_to_foods:
    37. self.not_to_foods.remove(next_food) # 移出未到的食物点
    38. self.now_position = foods[next_food] - foods[self.now] # 走的方向
    39. self.proportion = env.now # 出发时间
    40. def Next_Food(self): # 根据信息素和路径寻找下一个食物
    41. len_not_to_foods = len(self.not_to_foods) # 没选择的食物个数
    42. probability = np.zeros(len_not_to_foods) # 选择食物的概率
    43. for i in range(len_not_to_foods):
    44. # 选一个路径的概率是该点信息素^信息素权重*(1/路径长)^路径权重/总和,这里后面会用random.choices就不用除了
    45. probability[i] = pheromone[self.now][self.not_to_foods[i]] ** pheromone_w + (
    46. 1 / distance[self.now][self.not_to_foods[i]]) ** distance_w
    47. next_food = random.choices(self.not_to_foods, probability) # 根据权重选择下一个点
    48. return next_food
    49. def Change_Pheromone(self): # 更新信息素矩阵,先添加到一个临时变量中,信息素矩每隔一段时间后自己会挥发并将这个变量添加进去。
    50. for i in range(foods_n):
    51. new_pheromone[self.route[i]][self.route[i + 1]] += pheromone_Q / self.sum_len
    52. new_pheromone[self.route[i + 1]][self.route[i]] = new_pheromone[self.route[i]][self.route[i + 1]]
    53. def run(self): # 蚂蚁出动
    54. global min_distance, min_route
    55. while True:
    56. while self.not_to_foods: # 未空还能找食物
    57. next_food = self.Next_Food()[0]
    58. self.Set_Now(next_food) # 根据信息素和路径去下一个食物位置
    59. yield env.timeout(distance[self.now][next_food]) # 等待到达终点
    60. self.now = next_food # 到达位置
    61. # 没有食物可以去了
    62. next_food = self.route[0]
    63. self.Set_Now(next_food) # 去初始点
    64. yield env.timeout(distance[self.now][next_food]) # 等待到达终点
    65. self.now = next_food # 到达位置
    66. self.Change_Pheromone() # 到了初始点之后,更新信息素
    67. if min_distance > self.sum_len:
    68. min_distance = self.sum_len
    69. min_route = self.route
    70. self.Initial_Position(random.randint(0,foods_n-1)) # 重新初始化,再来一轮
    71. def Change_Pheromone(env):
    72. global pheromone, new_pheromone
    73. while True:
    74. # 每隔一段时间,挥发,更新信息素,
    75. # 我想要平均走完一波蚂蚁更新一波,那么,我大概需要等待平均总路长的时间(因为假设蚂蚁1m/s)这里就用(foods_n+1)/2,两点间平均距离大概是0.5
    76. yield env.timeout((foods_n + 1) / 2)
    77. pheromone = np.add((1 - volatilize) * pheromone, new_pheromone)
    78. new_pheromone = np.zeros((foods_n, foods_n))
    79. # 初始化函数,初始化蚁群,食物,信息素矩阵,
    80. def Initialization(ants_n, foods_n, dimension):
    81. '''
    82. :param ants_n: 蚁群大小
    83. :param foods_n: 食物数目
    84. :param dimension: 维度
    85. :return: 初始化蚁群,食物,信息素矩阵,距离矩阵
    86. '''
    87. ants = [Ant() for _ in range(ants_n)] # 蚁群
    88. np.random.seed(0) # 随机数种子,让生成的位置一致,你可以删去
    89. foods = np.random.rand(foods_n, dimension) # 所有食物位置,支持高维,不过绘图只有二维(python三维图太卡了),只测试二维
    90. pheromone = np.zeros((foods_n, foods_n))+2 # 信息素矩阵初始为路径长度平均数的倒数之类的
    91. distance = np.zeros((foods_n, foods_n)) # 距离矩阵
    92. for i in range(foods_n): # 计算欧氏距离
    93. for j in range(foods_n):
    94. if i > j: # 以前算过了,抄过来
    95. distance[i][j] = distance[j][i]
    96. continue
    97. if i == j: # 一样的位置就是0,不算了
    98. continue
    99. distance[i][j] = np.sqrt(np.sum((foods[i] - foods[j]) ** 2))
    100. return ants, foods, pheromone, distance
    101. def plt_Refresh(): # 绘图
    102. while True:
    103. plt.clf() # 清屏
    104. plt.xlim(0, 1)
    105. plt.ylim(-0.1, 1)
    106. # 绘图
    107. plt.text(0.05, -0.05, "now_time = " + str(env.now))
    108. for i in range(foods_n): # 所有的食物点
    109. for j in range(i):
    110. plt.plot([foods[i][0],foods[j][0]],[foods[i][1],foods[j][1]],linewidth=pheromone[i][j]/100)
    111. plt.scatter(foods[i][0], foods[i][1], 100)
    112. plt.text(foods[i][0], foods[i][1], i)
    113. for i in range(ants_n): # 所有的蚂蚁点
    114. # 到起始食物点,然后加上(起始和终点的向量乘以(当前时间-出发时间)/路长)就是蚂蚁位置,
    115. if ants[i].now == ants[i].next_food:
    116. position = foods[ants[i].now]
    117. else:
    118. position = foods[ants[i].now] + ants[i].now_position * (env.now - ants[i].proportion) / \
    119. distance[ants[i].now][ants[i].next_food]
    120. plt.scatter(position[0], position[1])
    121. plt.text(position[0], position[1], i)
    122. # 刷新图形
    123. plt.draw()
    124. plt.pause(time_particles)
    125. yield env.timeout(time_particles)
    126. def Firing(env): # env启动
    127. env.process(Change_Pheromone(env)) # 启动信息素矩阵
    128. env.process(plt_Refresh()) # 启动绘图
    129. for i in range(ants_n): # 启动所有蚂蚁
    130. env.process(ants[i].run())
    131. ants_n = 15 # 蚂蚁数目,一般1.5*foods_n
    132. foods_n = 10 # 食物数目
    133. dimension = 2 # 维度,建议2,因为绘图只搞了2
    134. min_distance = float("inf") # 最小距离
    135. min_route = [] # 最小路径
    136. # 一些超参数
    137. pheromone_w = 1 # 信息素权重
    138. distance_w = 6 # 距离权重
    139. volatilize = 0.5 # 信息素挥发比例
    140. pheromone_Q = 10 # 总信息素
    141. run_time = 100 # 模拟时间
    142. time_particles = 0.05 # 绘图间隔
    143. new_pheromone = np.zeros((foods_n, foods_n))
    144. # 绘图
    145. plt.figure()
    146. plt.pause(10) # 方便我录屏,等10s再出画面你们要删去
    147. env = simpy.Environment() # 设置环境并启动模拟
    148. ants, foods, pheromone, distance = Initialization(ants_n, foods_n, dimension) # 初始化,通常,蚁群数目是食物数目1.5倍
    149. print(foods)
    150. Firing(env) # 开火,启动(添加要模拟的函数)
    151. env.run(until=run_time) # 运行模拟
    152. print(min_route,min_distance)
    153. plt.show() # 遍历完成后不消失

    自己用建议删去 plt.pause(10)这一行,同时,可以调大time_particles,

    这是结果:

    视频审核还没通过,通过了就放出来

  • 相关阅读:
    X86函数调用模型分析
    OpenCV(三十九):积分图像
    day46 完全背包理论基础 518. 零钱兑换 II 377. 组合总和 Ⅳ
    L2t*+NPS
    Basler相机全部型号详细参数
    JS中的对象
    thinkphp使用递归创建树形菜单
    【JavaSE基础】对象的构造及初始化
    使用 MAUI 在 Windows 和 Linux 上绘制 PPT 的图表
    【SCA 开源组件漏洞整改】记录遇到的问题
  • 原文地址:https://blog.csdn.net/weixin_58196051/article/details/134541714