探索群体智慧:蚁群算法(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()  # 显示整个图窗

五、总结

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

相关推荐
Hello.Reader2 小时前
PySpark 依赖管理集群环境下如何分发 Python 包
开发语言·python
Rabitebla2 小时前
排序算法专题(一):插入排序 & 希尔排序
数据结构·算法·排序算法
南境十里·墨染春水10 小时前
C++传记(面向对象)虚析构函数 纯虚函数 抽象类 final、override关键字
开发语言·c++·笔记·算法
2301_7971727510 小时前
基于C++的游戏引擎开发
开发语言·c++·算法
有为少年11 小时前
告别“唯语料论”:用合成抽象数据为大模型开智
人工智能·深度学习·神经网络·算法·机器学习·大模型·预训练
AI医影跨模态组学11 小时前
J Transl Med(IF=7.5)苏州大学附属第一医院秦颂兵教授等团队:基于机器学习影像组学的食管鳞癌预后评估列线图
人工智能·深度学习·机器学习·ct·医学·医学影像
比昨天多敲两行11 小时前
C++ 二叉搜索树
开发语言·c++·算法
Season45011 小时前
C++11之正则表达式使用指南--[正则表达式介绍]|[regex的常用函数等介绍]
c++·算法·正则表达式
Tisfy12 小时前
LeetCode 2839.判断通过操作能否让字符串相等 I:if-else(两两判断)
算法·leetcode·字符串·题解