蟻群算法是模擬螞蟻覓食行為的仿生優化算法,原理是信息素的正反饋機制,螞蟻通過釋放信息素來引導同伴找到最短路徑。把問題的元素抽象為多條路徑,每次迭代時為每只螞蟻構建一個解決方案,該解決方案對應一條完整的路徑,每次迭代后對所有路徑上的信息素按一定比例模擬自然蒸發,避免局部最優,然后找出當前的最優路徑進行信息素增強,在之后的迭代中螞蟻就會傾向于選擇信息素濃度高的路徑,經過多次迭代后,找出全局的最優路徑。該算法通常用于解決旅行商等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;}}}
}
該算法還可以用于解決覆蓋設計問題