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
