蚁群优化算法

1.基本原理

蚁群算法模拟了自然界中蚂蚁寻找食物的群体智慧。蚂蚁在行进途中会释放信息素 ,在寻找路径时,蚂蚁倾向于选择信息素浓度更高的路段。由于短路径往返耗时短、单位时间内通过的次数多,其信息素堆积速度 远超长路径,而挥发损失相对较小。这种"正反馈"机制使得信息素强度在最优路径上迅速跃升,最终引导整个群体收敛至全局最优解。

2.代码实现

python 复制代码
# -*- coding:utf-8 -*-

import time
import platform

import torch
import numpy as np
import matplotlib.pyplot as plt


system = platform.system()
if system == "Darwin":  # macOS
    plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
elif system == "Windows":
    plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False


class ACOConfig:
    def __init__(self):
        self.ant_num = 60           # 蚂蚁数量
        self.city_num = 50          # 城市数量
        self.max_iter = 200         
        self.alpha = 1.0            # 信息素重要程度
        self.beta = 4.0             # 启发函数重要程度
        self.rho = 0.1              # 挥发系数
        self.Q = 10.0               # 信息素强度基数
        self.p_min = 0.01           # 最小信息素限制
        self.p_max = 5.0            # 最大信息素限制
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class ACO:
    def __init__(self, config):
        self.config = config
        
        self.city_pos = torch.rand((config.city_num, 2), device=config.device) * 100
        self.dist_mat = torch.cdist(self.city_pos, self.city_pos, p=2)
        
        # 初始化信息素
        self.pheromone_mat = torch.full((config.city_num, config.city_num), config.p_max, device=config.device)
        # 计算启发信息 1/d
        self.heuristic_mat = 1.0 / (self.dist_mat + torch.eye(config.city_num, device=config.device))
        
        self.best_path = None
        self.best_path_length = float('inf')
        self.history = []

    def _select_next_cities(self, current_cities, allowed_mask):
        # 选择得分
        p = self.pheromone_mat[current_cities, :] ** self.config.alpha
        h = self.heuristic_mat[current_cities, :] ** self.config.beta
        score = p * h * allowed_mask
        
        # 轮盘赌采样
        probs = score / (torch.sum(score, dim=1, keepdim=True) + 1e-10)
        return torch.multinomial(probs, 1).squeeze(1)

    def _get_path_lengths(self, paths):
        next_paths = torch.roll(paths, shifts=-1, dims=1)
        # 提取路径距离
        lengths = self.dist_mat[paths, next_paths] 
        return torch.sum(lengths, dim=1)

    def _update_pheromone(self, paths, lengths, iter_best_idx):
        self.pheromone_mat *= (1 - self.config.rho)
        
        delta_p = self.config.Q / lengths
        from_nodes = paths.flatten()
        to_nodes = torch.roll(paths, shifts=-1, dims=1).flatten()
        updates = delta_p.repeat_interleave(self.config.city_num)
        
        self.pheromone_mat.index_put_((from_nodes, to_nodes), updates, accumulate=True)
        self.pheromone_mat.index_put_((to_nodes, from_nodes), updates, accumulate=True)
        
        self.pheromone_mat = torch.clamp(self.pheromone_mat, self.config.p_min, self.config.p_max)
        
        self.pheromone_mat.fill_diagonal_(0)

    def run(self):
        print(f"执行设备: {self.config.device} | 城市规模: {self.config.city_num}")
        start = time.time()
        
        for i in range(self.config.max_iter):
            # 每代初始状态
            paths = torch.zeros((self.config.ant_num, self.config.city_num), dtype=torch.long, device=self.config.device)
            paths[:, 0] = torch.randint(0, self.config.city_num, (self.config.ant_num,), device=self.config.device)
            
            mask = torch.ones((self.config.ant_num, self.config.city_num), device=self.config.device)
            ant_idx = torch.arange(self.config.ant_num, device=self.config.device)
            mask[ant_idx, paths[:, 0]] = 0
            
            # 构建路径
            for step in range(1, self.config.city_num):
                next_c = self._select_next_cities(paths[:, step-1], mask)
                paths[:, step] = next_c
                mask[ant_idx, next_c] = 0
            
            lengths = self._get_path_lengths(paths)
            
            # 找到本代最优
            val, idx = torch.min(lengths, dim=0)
            if val < self.best_path_length:
                self.best_path_length = val.item()
                self.best_path = paths[idx].cpu().clone()
            
            self.history.append(self.best_path_length)
            self._update_pheromone(paths, lengths, idx)
            
            if (i + 1) % 20 == 0:
                print(f"迭代 [{i+1:03d}] 最优长度: {self.best_path_length:.4f}")

        print(f"\n优化完成!耗时: {time.time()-start:.2f}s")

    def plot(self):
        
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
        
        # 绘制路径
        coords = self.city_pos.cpu().numpy()
        p = self.best_path.numpy()
        p = np.append(p, p[0])
        ax1.plot(coords[p, 0], coords[p, 1], 'r-o', markersize=6, linewidth=1)
        ax1.set_title(f"最佳路径图 (长度: {self.best_path_length:.2f})")
        
        # 绘制收敛曲线
        ax2.plot(self.history, color='blue')
        ax2.set_title("收敛曲线")
        ax2.set_xlabel("迭代")
        ax2.set_ylabel("距离")
        
        plt.tight_layout()
        plt.show()

if __name__ == "__main__":
    conf = ACOConfig()
    model = ACO(conf)
    model.run()
    model.plot()

运行效果

bash 复制代码
执行设备: cpu | 城市规模: 50
迭代 [020] 最优长度: 607.0824
迭代 [040] 最优长度: 550.5231
迭代 [060] 最优长度: 549.9612
迭代 [080] 最优长度: 549.9612
迭代 [100] 最优长度: 545.2683
迭代 [120] 最优长度: 545.2683
迭代 [140] 最优长度: 545.2683
迭代 [160] 最优长度: 545.2683
迭代 [180] 最优长度: 545.2683
迭代 [200] 最优长度: 545.2683

优化完成!耗时: 0.99s
相关推荐
小和尚同志8 小时前
AI 自动化测试探索(二):Chrome-devtools MCP
人工智能·e2e·aigc
花酒锄作田8 小时前
Pydantic校验配置文件
python
hboot8 小时前
AI工程师第四课 - 深度学习入门
pytorch·python·神经网络
冬奇Lab10 小时前
Workflow 系列(02):设计范式——四层架构、三种 Context 传递模式与确认门设计
人工智能·agent·工作流引擎
冬奇Lab10 小时前
每日一个开源项目(第145篇):Trellis - 把项目记忆、规范和任务上下文持久化进代码仓库
人工智能·开源·资讯
有道AI情报局10 小时前
Harness即产品
人工智能·agent
罗西的思考11 小时前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
IT_陈寒12 小时前
SpringBoot自动配置的坑,我的API突然就404了
前端·人工智能·后端
笃行35012 小时前
从零到上线:用 EdgeOne Makers + CodeBuddy 搭一个「对账核对员」AI Agent
人工智能
用户68563262086913 小时前
Claude Code 乱猜字段名?我给它写了一个"数据库查询约束 Skill"
人工智能