探索群体智慧:蚁群算法(ACO)从原理到实践——python实现

探索群体智慧:蚁群算法(ACO)从原理到实践

一、算法简介

蚁群算法(Ant Colony Optimization, ACO)是一种源于自然界蚂蚁觅食行为的启发式优化算法,由Marco Dorigo在1992年首次提出。它通过模拟蚂蚁群体在寻找食物过程中留下的信息素,实现个体间的间接通信与协作,从而找到问题的最优解。ACO在解决组合优化问题,尤其是旅行商问题(TSP)、车辆路径问题(VRP)等方面表现出色。

二、核心思想与原理

想象一下,一群蚂蚁从蚁巢出发寻找食物。起初,它们随机探索。一旦有蚂蚁找到食物,它会在返回蚁巢的路径上释放一种名为**信息素(Pheromone)**的化学物质。

  • 路径强化:路径越短,蚂蚁往返一次的时间就越短,单位时间内留下的信息素浓度就越高。
  • 概率选择:其他蚂蚁在选择路径时,会倾向于选择信息素浓度更高的路径。
  • 正反馈:这种正反馈机制使得越来越多的蚂蚁聚集到较短的路径上,从而逐步逼近最优解。
  • 信息素挥发:同时,为了避免算法过早陷入局部最优,路径上的信息素会随时间挥发。

三、算法核心步骤与公式

1. 路径构建(状态转移)

每只蚂蚁根据路径上的信息素浓度启发式信息 (如城市间的距离)来概率性地选择下一个要访问的节点。蚂蚁 kkk 在时间 ttt 从节点 iii 转移到节点 jjj 的概率 Pijk(t)P_{ij}^k(t)Pijk(t) 由以下公式决定:

Pijk(t)=[τij(t)]α⋅[ηij]β∑s∈allowedk[τis(t)]α⋅[ηis]β P_{ij}^k(t) = \frac{[\tau_{ij}(t)]^\alpha \cdot [\eta_{ij}]^\beta}{\sum_{s \in \text{allowed}k} [\tau{is}(t)]^\alpha \cdot [\eta_{is}]^\beta} Pijk(t)=∑s∈allowedk[τis(t)]α⋅[ηis]β[τij(t)]α⋅[ηij]β

其中:

  • τij(t)\tau_{ij}(t)τij(t):路径 (i,j)(i, j)(i,j) 在时间 ttt 的信息素浓度。
  • ηij\eta_{ij}ηij:启发式信息,对于TSP问题,通常是距离的倒数 (1/dij1/d_{ij}1/dij)。
  • α,β\alpha, \betaα,β:两个参数,分别控制信息素和启发式信息的重要性。
  • allowedk\text{allowed}_kallowedk:蚂蚁 kkk 尚未访问的节点集合。

2. 信息素更新

当所有蚂蚁完成一次路径构建后,全局信息素会进行更新,包括挥发增加两个过程。

τij(t+1)=(1−ρ)τij(t)+Δτij \tau_{ij}(t+1) = (1-\rho)\tau_{ij}(t) + \Delta\tau_{ij} τij(t+1)=(1−ρ)τij(t)+Δτij

其中:

  • ρ\rhoρ:信息素挥发率 (0<ρ<10 < \rho < 10<ρ<1)。
  • Δτij\Delta\tau_{ij}Δτij:所有蚂蚁在路径 (i,j)(i, j)(i,j) 上留下的信息素增量之和。通常,路径越短的蚂蚁,留下的信息素越多。

3. 算法流程

  1. 初始化:将蚂蚁随机放置在不同起点,并初始化所有路径上的信息素。
  2. 路径构建:所有蚂蚁根据概率选择公式完成各自的路径。
  3. 信息素更新:根据所有蚂蚁构建的路径长度,更新全局信息素。
  4. 终止判断:重复步骤2和3,直到满足终止条件(如达到最大迭代次数)。

四、经典问题与例题:旅行商问题 (TSP)

问题描述:旅行商问题(Traveling Salesman Problem, TSP)是蚁群算法最经典的求解场景。问题要求找到一条访问每个城市一次并最终返回起点的最短路径。

具体例题

条件:

假设有5个城市,其二维坐标如下表所示:

城市编号 X坐标 Y坐标
1 0 0
2 1 5
3 3 2
4 8 1
5 6 6

求解目标:

一个旅行商从城市1出发,需要访问其余所有4个城市各一次,最后再返回城市1。请使用蚁群算法找到一条总距离最短的路线。

算法参数(参考):

  • 蚂蚁数量:10
  • 信息素重要程度因子 α\alphaα:1
  • 启发函数重要程度因子 β\betaβ:5
  • 信息素挥发率 ρ\rhoρ:0.5
  • 迭代次数:100

完整python代码

python 复制代码
import numpy as np  # 导入NumPy库,用于进行科学计算
import matplotlib.pyplot as plt  # 导入matplotlib的pyplot模块,用于数据可视化
import sys  # 导入sys模块,用于访问系统特定的参数和函数

# 设置matplotlib支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False    # 设置正常显示负号,以防坐标轴负数出错

# --- 问题定义 ---
# 定义5个城市的二维坐标
city_coordinates = np.array([
    [0, 0],  # 城市1
    [1, 5],  # 城市2
    [3, 2],  # 城市3
    [8, 1],  # 城市4
    [6, 6]   # 城市5
])

# --- 蚁群算法参数设置 ---
ant_count = 10      # 定义蚂蚁的数量
alpha = 1           # 定义信息素重要程度因子
beta = 5            # 定义启发函数重要程度因子(通常大于alpha)
rho = 0.5           # 定义信息素挥发率
Q = 100             # 定义信息素增加强度系数
max_iter = 100      # 定义最大迭代次数

num_cities = len(city_coordinates)  # 获取城市数量

# --- 计算城市间距离矩阵 ---
distance_matrix = np.zeros((num_cities, num_cities))  # 初始化一个全零的距离矩阵
for i in range(num_cities):  # 遍历所有城市
    for j in range(i, num_cities):  # 再次遍历城市,构建对称矩阵
        # 计算两个城市间的欧氏距离
        dist = np.linalg.norm(city_coordinates[i] - city_coordinates[j])
        distance_matrix[i, j] = distance_matrix[j, i] = dist  # 将距离存入距离矩阵

# --- 初始化信息素矩阵和启发式矩阵 ---
pheromone_matrix = np.ones((num_cities, num_cities))  # 初始化信息素矩阵,所有路径初始信息素为1
# 计算启发式矩阵 (eta),其值为距离的倒数
# 为了防止除以零错误,在分母上加上一个极小的数
heuristic_matrix = 1.0 / (distance_matrix + sys.float_info.epsilon)

# --- 记录全局最佳路径和其长度 ---
best_path = None  # 用于存储找到的最优路径
best_path_length = float('inf')  # 初始化最优路径长度为无穷大
best_length_history = [] # 用于记录每次迭代的最优长度,以便后续绘图

# --- 蚁群算法主循环 ---
for iteration in range(max_iter):  # 开始迭代
    ant_paths = []  # 存储当前迭代中每只蚂蚁的路径
    ant_path_lengths = []  # 存储当前迭代中每只蚂蚁的路径长度

    # 每只蚂蚁独立构建一条路径
    for ant in range(ant_count):
        current_city = np.random.randint(num_cities)  # 为蚂蚁随机选择一个起始城市
        path = [current_city]  # 将起始城市加入路径列表
        unvisited_cities = list(range(num_cities))  # 创建一个包含所有城市的待访问列表
        unvisited_cities.remove(current_city)  # 从待访问列表中移除起始城市
        path_length = 0  # 初始化当前路径长度为0

        # 当还有未访问的城市时,继续构建路径
        while unvisited_cities:
            probabilities = []  # 存储从当前城市到各未访问城市的转移概率
            # 遍历所有未访问的城市,计算转移概率
            for next_city in unvisited_cities:
                # 概率计算公式:(信息素^alpha) * (启发式信息^beta)
                prob = (pheromone_matrix[current_city, next_city] ** alpha) * \
                       (heuristic_matrix[current_city, next_city] ** beta)
                probabilities.append(prob)  # 将计算出的概率值加入列表
            
            probabilities = probabilities / np.sum(probabilities)  # 对概率进行归一化,使其总和为1
            
            # 根据归一化后的概率随机选择下一个要访问的城市
            next_city = np.random.choice(unvisited_cities, p=probabilities)
            
            path.append(next_city)  # 将新选择的城市加入路径
            path_length += distance_matrix[current_city, next_city]  # 累加路径长度
            unvisited_cities.remove(next_city)  # 从待访问列表中移除该城市
            current_city = next_city  # 更新当前所在城市
        
        path_length += distance_matrix[path[-1], path[0]]  # 路径构建完毕,加上从最后一个城市返回起点的距离
        ant_paths.append(path)  # 将该蚂蚁的完整路径存入列表
        ant_path_lengths.append(path_length)  # 将该蚂蚁的路径总长度存入列表

        # 如果当前蚂蚁的路径比已知的全局最优路径还要短,则更新全局最优解
        if path_length < best_path_length:
            best_path = path  # 更新最优路径
            best_path_length = path_length  # 更新最优路径长度

    # --- 更新信息素 ---
    pheromone_matrix *= (1 - rho)  # 1. 所有路径上的信息素进行挥发
    
    # 2. 根据所有蚂蚁的路径长度,在对应路径上增加信息素
    for i in range(ant_count):
        path = ant_paths[i]  # 获取一只蚂蚁的路径
        path_length = ant_path_lengths[i]  # 获取该路径的长度
        delta_pheromone = Q / path_length  # 计算该蚂蚁在路径上留下的信息素增量
        for j in range(num_cities - 1):  # 遍历路径上的每条边
            pheromone_matrix[path[j], path[j+1]] += delta_pheromone  # 增加信息素
            pheromone_matrix[path[j+1], path[j]] += delta_pheromone  # 因为是无向图,对称增加
        # 对连接终点和起点的路径也增加信息素
        pheromone_matrix[path[-1], path[0]] += delta_pheromone
        pheromone_matrix[path[0], path[-1]] += delta_pheromone

    best_length_history.append(best_path_length)  # 记录本次迭代结束后的全局最优长度
    
    print(f"迭代 {iteration + 1}: 最优路径长度 = {best_path_length:.2f}")  # 打印当前迭代信息

# --- 结果输出与可视化 ---
print("\n--- 最终结果 ---")  # 打印最终结果分隔符
# 打印最优路径,城市编号从1开始
print(f"最优路径: {' -> '.join(map(str, [city + 1 for city in best_path]))} -> {best_path[0] + 1}")
print(f"最短距离: {best_path_length:.2f}")  # 打印最短距离

# 创建一个包含两个子图的图窗
plt.figure(figsize=(12, 6))

# 1. 绘制最优路径图
plt.subplot(1, 2, 1)  # 创建第一个子图
plt.scatter(city_coordinates[:, 0], city_coordinates[:, 1], c='r', marker='o')  # 绘制所有城市的散点图
for i in range(num_cities):  # 遍历所有城市
    plt.text(city_coordinates[i, 0], city_coordinates[i, 1] + 0.2, str(i + 1))  # 在城市点旁边标注城市编号

# 根据最优路径的顺序连接城市,并闭合路径
best_path_coords = city_coordinates[best_path + [best_path[0]], :]
plt.plot(best_path_coords[:, 0], best_path_coords[:, 1], 'b-')  # 绘制蓝色连线
plt.title('蚁群算法最优路径')  # 设置子图标题
plt.xlabel('X坐标')  # 设置X轴标签
plt.ylabel('Y坐标')  # 设置Y轴标签
plt.grid(True)  # 显示网格

# 2. 绘制算法收敛曲线
plt.subplot(1, 2, 2)  # 创建第二个子图
plt.plot(best_length_history)  # 绘制最优路径长度随迭代次数变化的曲线
plt.title('算法收敛曲线')  # 设置子图标题
plt.xlabel('迭代次数')  # 设置X轴标签
plt.ylabel('最短路径长度')  # 设置Y轴标签
plt.grid(True)  # 显示网格

plt.tight_layout()  # 自动调整子图参数,使之填充整个图像区域
plt.show()  # 显示整个图窗

五、总结

蚁群算法通过模拟简单的个体行为涌现出复杂的群体智能,为解决复杂的组合优化问题提供了一种强大而灵活的工具。其分布式、自组织的特性使其具有很强的鲁棒性和扩展性。

相关推荐
輕華20 小时前
LSTM实战(上篇):微博情感分析——词表构建与数据集加载
人工智能·机器学习·lstm
2301_8152795220 小时前
SQL如何利用聚合函数生成业务分析指标_KPI计算基础教程
jvm·数据库·python
qq_3300379920 小时前
mysql如何排查Out of memory错误_mysql内存分配调优
jvm·数据库·python
好家伙VCC20 小时前
**发散创新:用Rust实现基于RAFT共识算法的轻量级分布式日志系统**在分布式系统中,**一致性协议**是保障数据可靠
java·分布式·python·rust·共识算法
小江的记录本20 小时前
【分布式】分布式核心组件——分布式ID生成:雪花算法、号段模式、美团Leaf、百度UidGenerator、时钟回拨解决方案
分布式·后端·算法·缓存·性能优化·架构·系统架构
weixin_4585801221 小时前
如何在 Go 中直接将 AST 编译为可执行二进制文件?
jvm·数据库·python
2301_816660211 天前
PHP怎么处理Eloquent Attribute Inference属性推断_Laravel从数据自动推导类型【操作】
jvm·数据库·python
第一程序员1 天前
数据工程 pipelines 实践
python·github
知行合一。。。1 天前
Python--05--面向对象(属性,方法)
android·开发语言·python
郝学胜-神的一滴1 天前
深度学习必学:PyTorch 神经网络参数初始化全攻略(原理 + 代码 + 选择指南)
人工智能·pytorch·python·深度学习·神经网络·机器学习