优化算法 | 蚁群算法(ACO)求解TSP问题(附Python代码)

一直关注公众号的小伙伴想必通过蚁群算法通俗讲解(附MATLAB代码)这篇推文已经了解了蚁群算法(ACO)的基本思想。今天在此基础上,我们分析ACO参数对算法性能的影响

实际案例

求解下述标星位置的最短路线,具体位置信息如下所示

cities = { "Oklahoma City": (392.8, 356.4), "Montgomery": (559.6, 404.8),
           "Saint Paul": (451.6, 186.0), "Trenton": (698.8, 239.6),
           "Salt Lake City": (204.0, 243.2), "Columbus": (590.8, 263.2),
           "Austin": (389.2, 448.4), "Phoenix": (179.6, 371.2),
           "Hartford": (719.6, 205.2), "Baton Rouge": (489.6, 442.0),
           "Salem": (80.0, 139.2), "Little Rock": (469.2, 367.2),
           "Richmond": (673.2, 293.6), "Jackson": (501.6, 409.6),
           "Des Moines": (447.6, 246.0), "Lansing": (563.6, 216.4),
           "Denver": (293.6, 274.0), "Boise": (159.6, 182.8),
           "Raleigh": (662.0, 328.8), "Atlanta": (585.6, 376.8),
           "Madison": (500.8, 217.6), "Indianapolis": (548.0, 272.8),
           "Nashville": (546.4, 336.8), "Columbia": (632.4, 364.8),
           "Providence": (735.2, 201.2), "Boston": (738.4, 190.8),
           "Tallahassee": (594.8, 434.8), "Sacramento": (68.4, 254.0),
           "Albany": (702.0, 193.6), "Harrisburg": (670.8, 244.0) }

ACO算法求解TSP问题Python代码

ACO算法求解TSP问题Python代码如下:

目录

▎实际案例

▎ACO算法求解TSP问题Python代码

▎ACO算法性能分析

04 | 蚂蚁数量

▎总结

▎参考

▎近期你可能错过了的好文章


Data.py读取位置数据,绘制路线图。

import numpy as np
import math
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

united_states_map = mpimg.imread("united_states_map.png")

def show_cities(path, w=12, h=8):
    """Plot a TSP path overlaid on a map of the US States & their capitals."""
    if isinstance(path, dict):      path = list(path.values())
    if isinstance(path[0][0], str): path = [item[1] for item in path]
    plt.imshow(united_states_map)
    for x0, y0 in path:
        plt.plot(x0, y0, 'y*', markersize=15)  # y* = yellow star for starting point
    plt.axis("off")
    fig = plt.gcf()
    fig.set_size_inches([w, h])

def show_path(path, starting_city=None, w=12, h=8):
    """Plot a TSP path overlaid on a map of the US States & their capitals."""
    if isinstance(path, dict):      path = list(path.values())
    if isinstance(path[0][0], str): path = [item[1] for item in path]

    starting_city = starting_city or path[0]
    x, y = list(zip(*path))
    # _, (x0, y0) = starting_city
    (x0, y0) = starting_city
    plt.imshow(united_states_map)
    # plt.plot(x0, y0, 'y*', markersize=15)  # y* = yellow star for starting point
    plt.plot(x + x[:1], y + y[:1])  # include the starting point at the end of path
    plt.axis("off")
    fig = plt.gcf()
    fig.set_size_inches([w, h])

def polyfit_plot(x, y, deg, **kwargs):
    coefficients = np.polyfit(x, y, deg, **kwargs)
    poly = np.poly1d(coefficients)
    new_x = np.linspace(x[0], x[-1])
    new_y = poly(new_x)
    plt.plot(x, y, "o", new_x, new_y)
    plt.xlim([x[0] - 1, x[-1] + 1])

    terms = []
    for p, c in enumerate(reversed(coefficients)):
        term = str(round(c, 1))
        if p == 1: term += 'x'
        if p >= 2: term += 'x^' + str(p)
        terms.append(term)
    plt.title(" + ".join(reversed(terms)))

def distance(xy1, xy2) -> float:
    if isinstance(xy1[0], str): xy1 = xy1[1]; xy2 = xy2[1];               # if xy1 == ("Name", (x,y))
    return math.sqrt( (xy1[0]-xy2[0])**2 + (xy1[1]-xy2[1])**2 )

def path_distance(path) -> int:
    if isinstance(path, dict):      path = list(path.values())            # if path == {"Name": (x,y)}
    if isinstance(path[0][0], str): path = [ item[1] for item in path ]   # if path == ("Name", (x,y))
    return int(sum(
        [ distance(path[i],  path[i+1]) for i in range(len(path)-1) ]
      + [ distance(path[-1], path[0]) ]                                   # include cost of return journey
    ))

cities = { "Oklahoma City": (392.8, 356.4), "Montgomery": (559.6, 404.8),
           "Saint Paul": (451.6, 186.0), "Trenton": (698.8, 239.6),
           "Salt Lake City": (204.0, 243.2), "Columbus": (590.8, 263.2),
           "Austin": (389.2, 448.4), "Phoenix": (179.6, 371.2),
           "Hartford": (719.6, 205.2), "Baton Rouge": (489.6, 442.0),
           "Salem": (80.0, 139.2), "Little Rock": (469.2, 367.2),
           "Richmond": (673.2, 293.6), "Jackson": (501.6, 409.6),
           "Des Moines": (447.6, 246.0), "Lansing": (563.6, 216.4),
           "Denver": (293.6, 274.0), "Boise": (159.6, 182.8),
           "Raleigh": (662.0, 328.8), "Atlanta": (585.6, 376.8),
           "Madison": (500.8, 217.6), "Indianapolis": (548.0, 272.8),
           "Nashville": (546.4, 336.8), "Columbia": (632.4, 364.8),
           "Providence": (735.2, 201.2), "Boston": (738.4, 190.8),
           "Tallahassee": (594.8, 434.8), "Sacramento": (68.4, 254.0),
           "Albany": (702.0, 193.6), "Harrisburg": (670.8, 244.0) }
cities = list(sorted(cities.items()))
print(len(cities))
show_cities(cities)

求解结果如下所示:

{'path_cost': 4118, 'ants_used': 1, 'epoch': 3717, 'round_trips': 1, 'clock': 0}
{'path_cost': 3942, 'ants_used': 86, 'epoch': 9804, 'round_trips': 2, 'clock': 0}
{'path_cost': 3781, 'ants_used': 133, 'epoch': 12887, 'round_trips': 3, 'clock': 0}
{'path_cost': 3437, 'ants_used': 135, 'epoch': 12900, 'round_trips': 3, 'clock': 0}
{'path_cost': 3428, 'ants_used': 138, 'epoch': 13019, 'round_trips': 3, 'clock': 0}
{'path_cost': 3192, 'ants_used': 143, 'epoch': 13339, 'round_trips': 3, 'clock': 0}
{'path_cost': 2981, 'ants_used': 193, 'epoch': 15646, 'round_trips': 4, 'clock': 0}
{'path_cost': 2848, 'ants_used': 198, 'epoch': 15997, 'round_trips': 4, 'clock': 0}
{'path_cost': 2760, 'ants_used': 199, 'epoch': 16030, 'round_trips': 4, 'clock': 0}
{'path_cost': 2757, 'ants_used': 216, 'epoch': 16770, 'round_trips': 4, 'clock': 0}
{'path_cost': 2662, 'ants_used': 222, 'epoch': 16973, 'round_trips': 4, 'clock': 0}
{'path_cost': 2600, 'ants_used': 263, 'epoch': 18674, 'round_trips': 5, 'clock': 0}
{'path_cost': 2504, 'ants_used': 276, 'epoch': 19218, 'round_trips': 5, 'clock': 0}
{'path_cost': 2442, 'ants_used': 284, 'epoch': 19338, 'round_trips': 5, 'clock': 0}
{'path_cost': 2313, 'ants_used': 326, 'epoch': 20982, 'round_trips': 6, 'clock': 0}
{'path_cost': 2226, 'ants_used': 728, 'epoch': 35648, 'round_trips': 12, 'clock': 0}
N=30  |  7074 -> 2240 |    2s | ants:  1727 | trips:   28 | distance_power=1
[('Albany', (702.0, 193.6)), ('Hartford', (719.6, 205.2)), ('Providence', (735.2, 201.2)), ('Boston', (738.4, 190.8)), ('Trenton', (698.8, 239.6)), ('Harrisburg', (670.8, 244.0)), ('Richmond', (673.2, 293.6)), ('Raleigh', (662.0, 328.8)), ('Columbia', (632.4, 364.8)), ('Atlanta', (585.6, 376.8)), ('Montgomery', (559.6, 404.8)), ('Tallahassee', (594.8, 434.8)), ('Baton Rouge', (489.6, 442.0)), ('Jackson', (501.6, 409.6)), ('Little Rock', (469.2, 367.2)), ('Oklahoma City', (392.8, 356.4)), ('Austin', (389.2, 448.4)), ('Phoenix', (179.6, 371.2)), ('Sacramento', (68.4, 254.0)), ('Salem', (80.0, 139.2)), ('Boise', (159.6, 182.8)), ('Salt Lake City', (204.0, 243.2)), ('Denver', (293.6, 274.0)), ('Des Moines', (447.6, 246.0)), ('Saint Paul', (451.6, 186.0)), ('Madison', (500.8, 217.6)), ('Lansing', (563.6, 216.4)), ('Columbus', (590.8, 263.2)), ('Indianapolis', (548.0, 272.8)), ('Nashville', (546.4, 336.8)), ('Nashville', (546.4, 336.8)), ('Albany', (702.0, 193.6))]

ACO算法性能分析

01 | 当不考虑启发因子,仅考虑信息素时

results = AntColonyRunner(cities, distance_power=0, min_time=30, verbose=True, plot=True)

求解结果如下:

{'path_cost': 6700, 'ants_used': 1, 'epoch': 6202, 'round_trips': 1, 'clock': 0}
{'path_cost': 6693, 'ants_used': 2, 'epoch': 6522, 'round_trips': 1, 'clock': 0}
{'path_cost': 5986, 'ants_used': 67, 'epoch': 13559, 'round_trips': 2, 'clock': 0}
{'path_cost': 5902, 'ants_used': 391, 'epoch': 50120, 'round_trips': 7, 'clock': 0}
{'path_cost': 5886, 'ants_used': 473, 'epoch': 59009, 'round_trips': 8, 'clock': 0}
{'path_cost': 5683, 'ants_used': 514, 'epoch': 62612, 'round_trips': 9, 'clock': 0}
{'path_cost': 5516, 'ants_used': 591, 'epoch': 72020, 'round_trips': 10, 'clock': 0}
{'path_cost': 5297, 'ants_used': 648, 'epoch': 77733, 'round_trips': 11, 'clock': 1}
{'path_cost': 5290, 'ants_used': 671, 'epoch': 79463, 'round_trips': 11, 'clock': 1}
{'path_cost': 5192, 'ants_used': 684, 'epoch': 80368, 'round_trips': 11, 'clock': 1}
{'path_cost': 4359, 'ants_used': 707, 'epoch': 83222, 'round_trips': 12, 'clock': 1}
N=30  |  7074 -> 4375 |    1s | ants:   958 | trips:   16 | distance_power=0 stop_factor=1.25

实验结果表明,仅考虑信息素,而不考虑启发因子,求解质量严重下降。

02 | 启发因子距离权重

启发因子距离权重n影响蚂蚁在选择下一节点时“看见”距离的能力,而不仅仅盲目地依赖信息素。接下来将距离权重n取不同值进行实验,验证其对求解质量的影响。

for distance_power in [-2.0, -1.0, 0.0, 0.5, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0, 5.0, 10.0]:
    result = AntColonyRunner(cities, distance_power=distance_power, timeout=60)

求解结果如下:

N=30  |  7074 -> 8225 |    1s | ants:   577 | trips:   10 | distance_power=-2.0 timeout=60
N=30  |  7074 -> 7957 |    1s | ants:   577 | trips:   10 | distance_power=-1.0 timeout=60
N=30  |  7074 -> 3947 |   22s | ants: 12439 | trips:  196 | distance_power=0.0 timeout=60
N=30  |  7074 -> 2558 |    7s | ants:  4492 | trips:   72 | distance_power=0.5 timeout=60
N=30  |  7074 -> 2227 |    2s | ants:  1168 | trips:   19 | distance_power=1.0 timeout=60
N=30  |  7074 -> 2290 |    4s | ants:  2422 | trips:   39 | distance_power=1.25 timeout=60
N=30  |  7074 -> 2367 |    1s | ants:   679 | trips:   11 | distance_power=1.5 timeout=60
N=30  |  7074 -> 2211 |    2s | ants:  1412 | trips:   23 | distance_power=1.75 timeout=60
N=30  |  7074 -> 2298 |    7s | ants:  4429 | trips:   70 | distance_power=2.0 timeout=60
N=30  |  7074 -> 2200 |    2s | ants:  1411 | trips:   23 | distance_power=3.0 timeout=60
N=30  |  7074 -> 2232 |    1s | ants:   758 | trips:   12 | distance_power=5.0 timeout=60
N=30  |  7074 -> 2240 |    1s | ants:   577 | trips:   10 | distance_power=10.0 timeout=60

实验结果表明:

1.当n取负数时,鼓励蚂蚁先到更远的节点,因此出现求解结果比随机路线更差的情况。

2.当n=0时,蚂蚁完全依赖信息素选择下一节点。

3.当n=1时,求解质量较好,但是在间隔紧密的节点周围存在一些“循环”路线。

4.当n=1.25~2时,算法收敛更快,求解质量更佳(通常默认n=2)

5.当n大于等于3时,算法收敛速度过快,导致算法搜索空间缩小,求解质量下降。

03 | 信息素权重

信息素权重影响信息素的相对差异被注意到的能力。接下来将信息素权重取不同值进行实验,验证其对求解质量的影响。

第1轮实验:

for distance_power in [0,1,2]:
    for pheromone_power in [-2.0, -1.0, 0.0, 0.5, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0, 5.0, 10.0]:
        result = AntColonyRunner(cities, distance_power=distance_power, pheromone_power=pheromone_power, time=0)
    print()

第1轮实验求解结果如下:

N=30  |  7074 -> 5477 |    1s | ants:   574 | trips:   10 | distance_power=0 pheromone_power=-2.0 time=0
N=30  |  7074 -> 5701 |    1s | ants:   688 | trips:   11 | distance_power=0 pheromone_power=-1.0 time=0
N=30  |  7074 -> 6039 |    2s | ants:   570 | trips:   10 | distance_power=0 pheromone_power=0.0 time=0
N=30  |  7074 -> 5970 |    2s | ants:   689 | trips:   11 | distance_power=0 pheromone_power=0.5 time=0
N=30  |  7074 -> 5628 |    2s | ants:   573 | trips:   10 | distance_power=0 pheromone_power=1.0 time=0
N=30  |  7074 -> 5246 |    2s | ants:   894 | trips:   15 | distance_power=0 pheromone_power=1.25 time=0
N=30  |  7074 -> 4035 |   13s | ants:  7217 | trips:  114 | distance_power=0 pheromone_power=1.5 time=0
N=30  |  7074 -> 4197 |    9s | ants:  4602 | trips:   73 | distance_power=0 pheromone_power=1.75 time=0
N=30  |  7074 -> 4328 |    3s | ants:  1788 | trips:   29 | distance_power=0 pheromone_power=2.0 time=0
N=30  |  7074 -> 4188 |    1s | ants:   700 | trips:   11 | distance_power=0 pheromone_power=3.0 time=0
N=30  |  7074 -> 5151 |    1s | ants:   577 | trips:   10 | distance_power=0 pheromone_power=5.0 time=0
N=30  |  7074 -> 5278 |    1s | ants:   577 | trips:   10 | distance_power=0 pheromone_power=10.0 time=0

N=30  |  7074 -> 4444 |    1s | ants:   561 | trips:   10 | distance_power=1 pheromone_power=-2.0 time=0
N=30  |  7074 -> 4035 |    1s | ants:   564 | trips:   10 | distance_power=1 pheromone_power=-1.0 time=0
N=30  |  7074 -> 4016 |    1s | ants:   788 | trips:   13 | distance_power=1 pheromone_power=0.0 time=0
N=30  |  7074 -> 2733 |    4s | ants:  1968 | trips:   32 | distance_power=1 pheromone_power=0.5 time=0
N=30  |  7074 -> 2263 |    3s | ants:  2424 | trips:   39 | distance_power=1 pheromone_power=1.0 time=0
N=30  |  7074 -> 2315 |    2s | ants:  1863 | trips:   30 | distance_power=1 pheromone_power=1.25 time=0
N=30  |  7074 -> 2528 |    1s | ants:  1107 | trips:   18 | distance_power=1 pheromone_power=1.5 time=0
N=30  |  7074 -> 2593 |    1s | ants:   615 | trips:   10 | distance_power=1 pheromone_power=1.75 time=0
N=30  |  7074 -> 2350 |    1s | ants:   631 | trips:   10 | distance_power=1 pheromone_power=2.0 time=0
N=30  |  7074 -> 2806 |    1s | ants:   680 | trips:   11 | distance_power=1 pheromone_power=3.0 time=0
N=30  |  7074 -> 3079 |    1s | ants:   577 | trips:   10 | distance_power=1 pheromone_power=5.0 time=0
N=30  |  7074 -> 3087 |    1s | ants:   577 | trips:   10 | distance_power=1 pheromone_power=10.0 time=0

N=30  |  7074 -> 3277 |    1s | ants:   558 | trips:   10 | distance_power=2 pheromone_power=-2.0 time=0
N=30  |  7074 -> 3191 |    1s | ants:   536 | trips:   10 | distance_power=2 pheromone_power=-1.0 time=0
N=30  |  7074 -> 2958 |    2s | ants:  1124 | trips:   19 | distance_power=2 pheromone_power=0.0 time=0
N=30  |  7074 -> 2229 |    4s | ants:  3027 | trips:   49 | distance_power=2 pheromone_power=0.5 time=0
N=30  |  7074 -> 2432 |    1s | ants:   560 | trips:   10 | distance_power=2 pheromone_power=1.0 time=0
N=30  |  7074 -> 2275 |    5s | ants:  4391 | trips:   70 | distance_power=2 pheromone_power=1.25 time=0
N=30  |  7074 -> 2320 |    3s | ants:  2194 | trips:   35 | distance_power=2 pheromone_power=1.5 time=0
N=30  |  7074 -> 2534 |    1s | ants:   778 | trips:   13 | distance_power=2 pheromone_power=1.75 time=0
N=30  |  7074 -> 2233 |    1s | ants:   938 | trips:   15 | distance_power=2 pheromone_power=2.0 time=0
N=30  |  7074 -> 2308 |    1s | ants:   897 | trips:   15 | distance_power=2 pheromone_power=3.0 time=0
N=30  |  7074 -> 2320 |    1s | ants:   645 | trips:   11 | distance_power=2 pheromone_power=5.0 time=0
N=30  |  7074 -> 2653 |    1s | ants:   577 | trips:   10 | distance_power=2 pheromone_power=10.0 time=0

第2轮实验:

for distance_power in [0,1,2]:
    for pheromone_power in [-2.0, -1.0, 0.0, 0.5, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0, 5.0, 10.0]:
        result = AntColonyRunner(cities, distance_power=distance_power, pheromone_power=pheromone_power, time=0)
    print()

第2轮实验求解结果如下:

N=30  |  7074 -> 5842 |    2s | ants:   577 | trips:   10 | distance_power=0 pheromone_power=-2.0 time=0
N=30  |  7074 -> 5897 |    3s | ants:  1080 | trips:   18 | distance_power=0 pheromone_power=-1.0 time=0
N=30  |  7074 -> 5798 |    1s | ants:   574 | trips:   10 | distance_power=0 pheromone_power=0.0 time=0
N=30  |  7074 -> 6059 |    1s | ants:   570 | trips:   10 | distance_power=0 pheromone_power=0.5 time=0
N=30  |  7074 -> 4068 |    7s | ants:  3562 | trips:   57 | distance_power=0 pheromone_power=1.0 time=0
N=30  |  7074 -> 3620 |    3s | ants:  2553 | trips:   41 | distance_power=0 pheromone_power=1.25 time=0
N=30  |  7074 -> 3994 |    5s | ants:  2822 | trips:   45 | distance_power=0 pheromone_power=1.5 time=0
N=30  |  7074 -> 4067 |    3s | ants:  2210 | trips:   35 | distance_power=0 pheromone_power=1.75 time=0
N=30  |  7074 -> 4232 |    3s | ants:  2057 | trips:   33 | distance_power=0 pheromone_power=2.0 time=0
N=30  |  7074 -> 4559 |    2s | ants:  1179 | trips:   19 | distance_power=0 pheromone_power=3.0 time=0
N=30  |  7074 -> 4566 |    1s | ants:   575 | trips:   10 | distance_power=0 pheromone_power=5.0 time=0
N=30  |  7074 -> 5565 |    1s | ants:   577 | trips:   10 | distance_power=0 pheromone_power=10.0 time=0

N=30  |  7074 -> 4377 |    1s | ants:   570 | trips:   10 | distance_power=1 pheromone_power=-2.0 time=0
N=30  |  7074 -> 4459 |    1s | ants:   535 | trips:   10 | distance_power=1 pheromone_power=-1.0 time=0
N=30  |  7074 -> 4269 |    1s | ants:   559 | trips:   10 | distance_power=1 pheromone_power=0.0 time=0
N=30  |  7074 -> 3144 |    1s | ants:   636 | trips:   11 | distance_power=1 pheromone_power=0.5 time=0
N=30  |  7074 -> 2371 |    2s | ants:  1265 | trips:   21 | distance_power=1 pheromone_power=1.0 time=0
N=30  |  7074 -> 2298 |    4s | ants:  2880 | trips:   47 | distance_power=1 pheromone_power=1.25 time=0
N=30  |  7074 -> 2337 |    7s | ants:  4058 | trips:   65 | distance_power=1 pheromone_power=1.5 time=0
N=30  |  7074 -> 2317 |    2s | ants:  1647 | trips:   27 | distance_power=1 pheromone_power=1.75 time=0
N=30  |  7074 -> 2386 |    2s | ants:   808 | trips:   13 | distance_power=1 pheromone_power=2.0 time=0
N=30  |  7074 -> 2434 |    1s | ants:   573 | trips:   10 | distance_power=1 pheromone_power=3.0 time=0
N=30  |  7074 -> 2776 |    1s | ants:   578 | trips:   10 | distance_power=1 pheromone_power=5.0 time=0
N=30  |  7074 -> 3255 |    1s | ants:   577 | trips:   10 | distance_power=1 pheromone_power=10.0 time=0

N=30  |  7074 -> 2748 |    1s | ants:   560 | trips:   10 | distance_power=2 pheromone_power=-2.0 time=0
N=30  |  7074 -> 3224 |    1s | ants:   542 | trips:   10 | distance_power=2 pheromone_power=-1.0 time=0
N=30  |  7074 -> 2958 |    1s | ants:   543 | trips:   10 | distance_power=2 pheromone_power=0.0 time=0
N=30  |  7074 -> 2705 |    2s | ants:   816 | trips:   14 | distance_power=2 pheromone_power=0.5 time=0
N=30  |  7074 -> 2205 |    2s | ants:  1033 | trips:   17 | distance_power=2 pheromone_power=1.0 time=0
N=30  |  7074 -> 2301 |    2s | ants:  1071 | trips:   17 | distance_power=2 pheromone_power=1.25 time=0
N=30  |  7074 -> 2248 |    2s | ants:  1171 | trips:   19 | distance_power=2 pheromone_power=1.5 time=0
N=30  |  7074 -> 2194 |    2s | ants:  1001 | trips:   16 | distance_power=2 pheromone_power=1.75 time=0
N=30  |  7074 -> 2371 |    1s | ants:   575 | trips:   10 | distance_power=2 pheromone_power=2.0 time=0
N=30  |  7074 -> 2338 |    2s | ants:  1310 | trips:   21 | distance_power=2 pheromone_power=3.0 time=0
N=30  |  7074 -> 2371 |    1s | ants:   576 | trips:   10 | distance_power=2 pheromone_power=5.0 time=0
N=30  |  7074 -> 2942 |    1s | ants:   577 | trips:   10 | distance_power=2 pheromone_power=10.0 time=0

第3轮实验:

for distance_power in [0,1,2]:
    for pheromone_power in [1.0, 1.1, 1.2, 1.3, 1.4]:
        result = AntColonyRunner(cities, distance_power=distance_power, pheromone_power=pheromone_power, time=0)
    print()

第3轮实验求解结果如下:

N=30  |  7074 -> 5107 |    1s | ants:   764 | trips:   13 | distance_power=0 pheromone_power=1.0 time=0
N=30  |  7074 -> 3393 |   15s | ants:  8420 | trips:  134 | distance_power=0 pheromone_power=1.1 time=0
N=30  |  7074 -> 3548 |   20s | ants: 12669 | trips:  200 | distance_power=0 pheromone_power=1.2 time=0
N=30  |  7074 -> 4267 |    2s | ants:   831 | trips:   14 | distance_power=0 pheromone_power=1.3 time=0
N=30  |  7074 -> 5194 |    1s | ants:   562 | trips:   10 | distance_power=0 pheromone_power=1.4 time=0

N=30  |  7074 -> 2231 |    3s | ants:  1507 | trips:   25 | distance_power=1 pheromone_power=1.0 time=0
N=30  |  7074 -> 2297 |    4s | ants:  2213 | trips:   36 | distance_power=1 pheromone_power=1.1 time=0
N=30  |  7074 -> 2399 |    5s | ants:  2586 | trips:   41 | distance_power=1 pheromone_power=1.2 time=0
N=30  |  7074 -> 2274 |    3s | ants:  1365 | trips:   22 | distance_power=1 pheromone_power=1.3 time=0
N=30  |  7074 -> 2323 |    2s | ants:  1164 | trips:   19 | distance_power=1 pheromone_power=1.4 time=0

N=30  |  7074 -> 2244 |    3s | ants:  1724 | trips:   28 | distance_power=2 pheromone_power=1.0 time=0
N=30  |  7074 -> 2240 |    3s | ants:  1518 | trips:   25 | distance_power=2 pheromone_power=1.1 time=0
N=30  |  7074 -> 2251 |    3s | ants:  1736 | trips:   28 | distance_power=2 pheromone_power=1.2 time=0
N=30  |  7074 -> 2259 |    1s | ants:   895 | trips:   15 | distance_power=2 pheromone_power=1.3 time=0
N=30  |  7074 -> 2257 |    6s | ants:  4397 | trips:   70 | distance_power=2 pheromone_power=1.4 time=0

实验结果表面:

1.负数使信息素被排斥,但是求解结果仍比随机结果更优。

2.蚂蚁对信息大于1的信息素较为敏感,稍微增加一点就会大幅度改进求解结果,但会增加算法求解时间。

3.通常默认信息素权重取值1.25。

04 | 蚂蚁数量

我们的直觉是蚂蚁数量越大,收敛速度更快,求解质量更好,那么结果究竟是否如此,下面的实验为我们揭晓答案。

for ant_count in range(0,16+1):
    AntColonyRunner(cities, ant_count=2**ant_count, time=60)

求解结果如下:

N=30  |  7074 -> 2711 |   60s | ants:  3722 | trips: 3722 | ant_count=1 time=60
N=30  |  7074 -> 2785 |   60s | ants:  6094 | trips: 3047 | ant_count=2 time=60
N=30  |  7074 -> 2228 |   60s | ants: 14719 | trips: 3682 | ant_count=4 time=60
N=30  |  7074 -> 2338 |   60s | ants: 23195 | trips: 2902 | ant_count=8 time=60
N=30  |  7074 -> 2465 |   60s | ants: 27378 | trips: 1714 | ant_count=16 time=60
N=30  |  7074 -> 2430 |   60s | ants: 41132 | trips: 1291 | ant_count=32 time=60
N=30  |  7074 -> 2262 |   60s | ants: 43098 | trips:  676 | ant_count=64 time=60
N=30  |  7074 -> 2210 |   60s | ants: 63140 | trips:  496 | ant_count=128 time=60
N=30  |  7074 -> 2171 |   60s | ants: 68796 | trips:  272 | ant_count=256 time=60
N=30  |  7074 -> 2196 |   60s | ants: 67791 | trips:  134 | ant_count=512 time=60
N=30  |  7074 -> 2203 |   60s | ants: 73140 | trips:   73 | ant_count=1024 time=60
N=30  |  7074 -> 2227 |   60s | ants: 81049 | trips:   41 | ant_count=2048 time=60
N=30  |  7074 -> 2196 |   60s | ants: 73486 | trips:   19 | ant_count=4096 time=60
N=30  |  7074 -> 2210 |   60s | ants: 71547 | trips:   10 | ant_count=8192 time=60
N=30  |  7074 -> 2176 |   60s | ants: 63254 | trips:    5 | ant_count=16384 time=60
N=30  |  7074 -> 2357 |   60s | ants: 33481 | trips:    2 | ant_count=32768 time=60
N=30  |  7074 -> 3490 |   61s | ants: 10391 | trips:    1 | ant_count=65536 time=60

实验结果表明:

1.蚂蚁数量在64~16384之间,ACO求解质量较佳。

2.当蚂蚁数量大于32768时,ACO求解质量急剧下降。

总结

我们通过这期推文分析了距离权重、信息素权重和蚂蚁数量对ACO性能的影响,初步结论为距离权重通常设为2信息素权重通常设为1.25蚂蚁数量取值范围为64~16384

参考

[1]https://www.kaggle.com/code/jamesmcguigan/ant-colony-optimization-algorithm/notebook

咱们下期再见

▎近期你可能错过了的好文章

新书上架 | 《MATLAB智能优化算法:从写代码到算法思想》

优化算法 | 灰狼优化算法(文末有福利)

优化算法 | 鲸鱼优化算法

遗传算法(GA)求解带时间窗的车辆路径(VRPTW)问题MATLAB代码

粒子群优化算法(PSO)求解带时间窗的车辆路径问题(VRPTW)MATLAB代码

  • 15
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
_GPIO_Port GPIOA #define IR_Pin GPIO_PIN_0 // 定义计时器和捕获寄存器 #define TIMx好的,以下是一个简单的ACO算法实现TSP优化问题的Python代码,您可以根据自 TIM2 #define TIMx_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE() #define TIMx_IRQn TIM2_IRQn #define TIMx_IRQHandler己的需求进行修改: ```python import numpy as np import matplotlib.pyplot as plt # 读取数据文件 def TIM2_IRQHandler #define TIMx_CHANNEL GPIO_PIN_0 #define TIMx_CHANNEL_GPIO_PORT GPIOA #define TIMx_CHANNEL_GPIO_PORT_CLK read_data(filename): with open(filename, 'r') as f: lines = f.readlines() node_num = int(lines[0_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define TIMx_CHANNEL_AF GPIO_AF1_TIM2 #define TIMx_CAPTURE_COMPARE_REGISTER CCR]) data = np.zeros((node_num, 2)) for i in range(1, node_num+1): line =1 // 初始化红外传感器 void ir_init(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_HandleTypeDef htim lines[i] parts = line.split() data[i-1][0] = float(parts[1]) data[i-1][; // 初始化红外传感器的GPIO口 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull =1] = float(parts[2]) return data # 计算距离矩阵 def calc_dist_matrix(data): node_num GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Pin = IR_Pin; HAL_GPIO_Init(IR_GPIO = data.shape[0] dist_matrix = np.zeros((node_num, node_num)) for i in range(node_num): for_Port, &GPIO_InitStruct); // 初始化计时器 TIMx_CLK_ENABLE(); TIMx_CHANNEL_GPIO_PORT_CLK_ENABLE j in range(i+1, node_num): dist_matrix[i][j] = np.linalg.norm(data[i] - data[j]) (); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ dist_matrix[j][i] = dist_matrix[i][j] return dist_matrix # ACO算法 def ACO(dist_matrix_HIGH; GPIO_InitStruct.Pin = TIMx_CHANNEL; GPIO_InitStruct.Alternate = TIMx_CHANNEL_AF; HAL_GPIO_Init(TIM, ant_num, max_iter, alpha, beta, rho, Q): node_num = dist_matrix.shape[0] pheromx_CHANNEL_GPIO_PORT, &GPIO_InitStruct); htim.Instance = TIMx; htim.Init.Prescaler = 0; one = np.ones((node_num, node_num)) / node_num best_path = [] best_length = np.inf for htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 65535; htim.Init.ClockDivision = i in range(max_iter): # 初始化蚂蚁 ants = np.zeros((ant_num, node_num), dtype=int) for TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim); HAL_TIM_IC_Init(&htim); TIM_IC_InitTypeDef sConfig; sConfig.ICPolarity = TIM_ICPOLARITY_FALLING; sConfig.ICSelection = TIM_ICSELECTION j in range(ant_num): start_node = np.random.randint(node_num) ants[j][0] = start_node #_DIRECTTI; sConfig.ICPrescaler = TIM_ICPSC_DIV1; sConfig.ICFilter = 0; HAL_TIM 蚂蚁寻路 for j in range(ant_num): for k in range(1, node_num): current_node = ants[j][k-1] # 计算每个节点的概率 p = pheromone[current_node] **_IC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim, TIM_CHANNEL_ alpha * (1 / dist_matrix[current_node]) ** beta p[ants[j][:k]] = 0 # 已经走过1); } // 计算电机的转速 uint16_t calculate_speed(void) { static uint16_t last_capture = 的节点概率设为0 # 根据概率选择下一个节点 next_node = np.random.choice(node_num,0; static uint16_t last_speed = 0; uint16_t speed; uint16_t capture = TIMx->TIMx_CAPTURE_COMPARE_REGISTER; if (capture < last_capture) { speed = (65535 - last_capture + capture) * 60 p=p / np.sum(p)) ants[j][k] = next_node # 更新信息素 delta_pheromone / 20; } else { speed = (capture - last_capture) * 60 / 20; } last_capture = np.zeros((node_num, node_num)) for j in range(ant_num): path_length = 0 for k = capture; if (speed < 200) { speed = last_speed; } last_speed = speed; return speed in range(node_num-1): current_node, next_node = ants[j][k], ants[j][k+1] path_length; } // 中断处理函数,用于测量电机的转速 void TIMx_IRQHandler(void) { HAL_TIM_IRQHandler += dist_matrix[current_node][next_node] path_length += dist_matrix[ants[j][-1]][ants[j][0]] if path(&htim); } ``` 4. 控制电机转速 根据期望转速和实际转速,我们_length < best_length: best_path = ants[j] best_length = path_length for k in range(node_num-1): current_node, next_node = ants[j][k], ants[j][k+1] delta_pheromone[current_node][next可以使用PID控制算法来计算电机的PWM值,从而控制电机转速。在程序中,_node] += Q / path_length delta_pheromone[next_node][current_node] = delta_pheromone[current_node][我们需要实现PID算法,并将其应用于控制电机转速。以下是一个控制电机转速next_node] delta_pheromone[ants[j][-1]][ants[j][0]] += Q / path_length delta_p的示例代码: ```c // 定义电机的GPIO口和PWM定时器 #define MOTOR_GPIO_Port GPIOB #define MOTOR_Pin GPIO_PIN_0 #define PWM_TIMx TIM3 #define PWM_TIMx_CLK_ENABLE() __HAL_RCC_TIM3_CLK_ENABLEheromone[ants[j][0]][ants[j][-1]] = delta_pheromone[ants[j][-1]][ants[j][() #define PWM_TIMx_CHANNEL GPIO_PIN_0 #define PWM_TIMx_CHANNEL_GPIO_PORT GPIO]] pheromone = pheromone * (1 - rho) + delta_pheromone * rho return #define PWM_TIMx_CHANNEL_AF GPIO_AF2_TIM3 #define PWM_TIMx_PRESCALER 0 #define PWM_TIMx_PERIOD 999 // 初始化电机和PWM定 best_path, best_length # 绘制路径图 def plot_path(data, path): plt.plot(data[:,0], data[:,1时器 void motor_init(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_HandleTypeDef htim; // 初始化电机], 'o') for i in range(len(path)-1): plt.plot(data[path[i:i+2],0], data[path[i的GPIO口 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed =:i+2],1], 'r--') plt.plot(data[path[-1],0], data[path[-1],1], 'r GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Pin = MOTOR_Pin; HAL_GPIO_Init(MOTOR_GPIO_Port, &GPIO_InitStruct); --') plt.show() if __name__ == '__main__': filename = 'att48.tsp' data = read_data(filename // 初始化PWM定时器 PWM_TIMx_CLK_ENABLE(); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull) dist_matrix = calc_dist_matrix(data) ant_num = 50 max_iter = 100 alpha = 1 = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Pin = PWM_TIMx_CHANNEL; GPIO_InitStruct beta = 5 rho = 0.1 Q = 1 best_path, best_length = A.Alternate = PWM_TIMx_CHANNEL_AF; HAL_GPIO_Init(PWM_TIMx_CHANNEL_GPIO_PORT, &GPIO_InitStruct); htimCO(dist_matrix, ant_num, max_iter, alpha, beta, rho, Q) print('最短路径长度:', best_length.Instance = PWM_TIMx; htim.Init.Prescaler = PWM_TIMx_PRESCALER; htim.Init.CounterMode = TIM) print('最短路径:', best_path) plot_path(data, best_path) ``` 代码中的`read_data`_COUNTERMODE_UP; htim.Init.Period = PWM_TIMx_PERIOD; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1函数用于读取TSP数据文件,`calc_dist_matrix`函数用于计算距离矩阵,`ACO`; HAL_TIM_PWM_Init(&htim); TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode = TIM_O函数为ACO算法的主要实现,`plot_path`函数用于绘制路径图。在代码中,我CMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY使用了att48.tsp测试集,您可以根据自己的需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值