蚁群算法是模拟蚂蚁觅食行为的仿生优化算法,原理是信息素的正反馈机制,蚂蚁通过释放信息素来引导同伴找到最短路径。把问题的元素抽象为多条路径,每次迭代时为每只蚂蚁构建一个解决方案,该解决方案对应一条完整的路径,每次迭代后对所有路径上的信息素按一定比例模拟自然蒸发,避免局部最优,然后找出当前的最优路径进行信息素增强,在之后的迭代中蚂蚁就会倾向于选择信息素浓度高的路径,经过多次迭代后,找出全局的最优路径。该算法通常用于解决旅行商等NP难问题,算法性能依赖参数(如信息素重要因子 α、启发式因子 β、挥发率 ρ 等),结果难以预测,有一定的玄学。
算法流程
集合覆盖问题
给定一个全集U和若干子集S1, S2, …, Sn,找到最少数量的子集,使得它们的并集等于U。例如:
全集 U = {1, 2, 3, 4, 5}
子集 S1 = {1, 3}, S2 = {2, 3}, S3 = {3, 4}, S4 = {1, 4, 5}
最优解:[S1, S3] 能覆盖所有元素的最小子集数量为2。
蚁群算法代码
import java.util.*;
public class AcoSetCover {
// 定义全集和子集
static Set<Integer> universe = new HashSet<>(Arrays.asList(1, 2, 3, 4,5));
static List<Set<Integer>> subsets = Arrays.asList(
new HashSet<>(Arrays.asList(1, 3)),
new HashSet<>(Arrays.asList(2, 3)),
new HashSet<>(Arrays.asList(3, 4)),
new HashSet<>(Arrays.asList(1, 4, 5))
);
static int numSubsets = subsets.size();
// 算法参数
static int m = 10; // 蚂蚁数量
static int maxIter = 10000; // 最大迭代次数
static double alpha = 1.0; // 信息素重要因子
static double beta = 2.0; // 启发式因子
static double rho = 0.1; // 信息素挥发率
static double Q = 1.0; // 信息素强度
static double[] pheromone; // 子集的信息素
public static void main(String[] args) {
initializePheromone();
List<Integer> bestSolution = null;
int bestSize = Integer.MAX_VALUE;
for (int iter = 0; iter < maxIter; iter++) {
List<List<Integer>> antSolutions = new ArrayList<>();
for (int ant = 0; ant < m; ant++) {
List<Integer> solution = constructSolution();
antSolutions.add(solution);
if (solution.size() < bestSize) {
bestSize = solution.size();
bestSolution = new ArrayList<>(solution);
}
}
updatePheromone(antSolutions);
}
System.out.println("全集: "+universe);
System.out.println("子集: "+subsets);
System.out.println("最优解子集下标: " + bestSolution);
for(int i:bestSolution){
System.out.println(subsets.get(i));
}
}
// 初始化信息素
static void initializePheromone() {
pheromone = new double[numSubsets];
Arrays.fill(pheromone, 1.0); // 初始信息素为1
}
// 蚂蚁构建解
static List<Integer> constructSolution() {
Set<Integer> covered = new HashSet<>();
List<Integer> solution = new ArrayList<>();
List<Integer> candidates = new ArrayList<>();
while (!covered.equals(universe)) {
candidates.clear();
for (int i = 0; i < numSubsets; i++) {
if (!solution.contains(i) && !Collections.disjoint(subsets.get(i), universe)) {
Set<Integer> subset = subsets.get(i);
if (!covered.containsAll(subset)) {
candidates.add(i);
}
}
}
if (candidates.isEmpty()) break;
// 计算选择概率
double[] probabilities = new double[candidates.size()];
double total = 0.0;
for (int i = 0; i < candidates.size(); i++) {
int subsetIdx = candidates.get(i);
double heuristic = (double) (subsets.get(subsetIdx).size() - covered.size()) / subsets.get(subsetIdx).size();
probabilities[i] = Math.pow(pheromone[subsetIdx], alpha) * Math.pow(heuristic, beta);
total += probabilities[i];
}
// 轮盘赌选择
double rand = Math.random() * total;
double cumulative = 0.0;
int selected = -1;
for (int i = 0; i < candidates.size(); i++) {
cumulative += probabilities[i];
if (cumulative >= rand) {
selected = candidates.get(i);
break;
}
}
// 更新覆盖集和解
solution.add(selected);
covered.addAll(subsets.get(selected));
}
return solution;
}
// 更新信息素
static void updatePheromone(List<List<Integer>> antSolutions) {
// 信息素挥发
for (int i = 0; i < numSubsets; i++) {
pheromone[i] *= (1 - rho);
}
// 蚂蚁释放信息素
for (List<Integer> solution : antSolutions) {
double delta = Q / solution.size();
for (int subsetIdx : solution) {
pheromone[subsetIdx] += delta;
}
}
}
}
该算法还可以用于解决覆盖设计问题