蚁群算法ACS处理旅行商问题TSP【Java实现】

1. 介绍

蚁群算法是一种群体智能算法,模拟了蚂蚁寻找食物时的行为,通过蚂蚁之间的信息交流和合作,最终实现全局最优解的寻找【是否找得到和迭代次数有关】。

蚁群算法的基本思想是将搜索空间看作一个由节点组成的图,每个节点代表一种可能的解决方案,而边则代表两个节点之间的关联关系。在这个图中,一只蚂蚁在搜索过程中会沿着路径移动,每经过一个节点,它会留下信息素,这些信息素会吸引其他蚂蚁跟随它的路径。同时,蚂蚁在移动的过程中也会依据信息素的浓度,更有可能选择路径上信息素浓度较高的节点。

蚂蚁群算法的核心是信息素的更新和挥发。信息素的更新是指当一只蚂蚁找到一个更优的解决方案时,它会在路径上留下更多的信息素,这样其他蚂蚁就更有可能跟随这个路径,从而加速搜索过程。信息素的挥发则是指在一定的时间间隔内,信息素会自然挥发,这样可以避免信息素积累过多导致搜索过早收敛。

蚂蚁群算法是一种高效的全局优化算法,广泛应用于组合优化、信号处理、机器学习、图形图像处理等领域。

2. 主体公式

蚁群算法中,蚂蚁到达每个点的概率是根据该点的信息素浓度和距离来计算的。具体地说,设当前蚂蚁所在的城市为 i i i ,已经访问过的城市集合为 J J J ,未访问过的城市集合为 K K K ,则蚂蚁由 i i i 到达城市 j j j 的概率可以用如下公式计算:
p i , j = [ τ i , j ] α [ η i , j ] β ∑ k ∈ K [ τ i , k ] α [ η i , k ] β p_{i,j}=\frac{[\tau_{i,j}]^{\alpha}[\eta_{i,j}]^{\beta}}{\sum\limits_{k\in K} [\tau_{i,k}]^{\alpha}[\eta_{i,k}]^{\beta}} pi,j=kK[τi,k]α[ηi,k]β[τi,j]α[ηi,j]β

其中, τ i , j \tau_{i,j} τi,j 表示从城市 i i i 到城市 j j j 的信息素浓度, η i , j \eta_{i,j} ηi,j 表示从城市 i i i 到城市 j j j 的距离的倒数, α \alpha α β \beta β 分别表示信息素重要程度因子和距离重要程度因子,通常 α \alpha α β \beta β 的值都为正数。在选择下一个要访问的城市时,蚂蚁会根据这个概率进行随机选择,不难发现,信息素浓度越大,城市间的距离越短,对应的概率就会越大。

对于一只蚂蚁走过的路径上的每条边 $ (i,j)$ ,它在该边上留下的信息素增量为:
Δ τ i j = q L k \Delta \tau_{ij} = \dfrac{q}{L_k} Δτij=Lkq
其中, q q q 是信息素增加强度系数, L k L_k Lk 表示第 k k k 只蚂蚁完成路径的长度。

参数代码形式如下👇:

	// 距离矩阵
	private double[][] distance;
	// 信息素矩阵
	private double[][] pheromone; 
	// 城市数量
	private int cityNum; 
	// 蚂蚁数量
	private int antNum; 
	// 最大迭代次数
	private int maxGen; 
	// 信息素因子
	private double alpha; 
	// 距离重要程度因子
	private double beta; 
	// 信息素挥发因子
	private double rho; 
	// 信息素增加强度系数
	private double q; 
	// 最优路径
	private int[] bestTour; 
	// 最优路径长度
	private double bestLength; 

3. 算法实现步骤

  1. 初始化信息素矩阵,初始时各路径上信息素浓度相同

    	// 初始化信息素矩阵
    	private void initPheromone() {
    		// 信息素初始值
    		double initPheromone = 1.0 / (cityNum * distance[0][1]);
    		for (int i = 0; i < cityNum; i++) {
    			for (int j = 0; j < cityNum; j++) {
    				// 各条路径的信息素浓度
    				pheromone[i][j] = initPheromone;
    			}
    		}
    	}
    
  2. 第一只蚂蚁从随机一个城市出发,综合信息素浓度以及到达各城市的距离,计算得到公式 p i j p_{ij} pij 的分子,存储在数组 p p p 中,同时将分母用累加和 s u m sum sum 存储

    一个圆形转盘区域标记为 [ 0 , 1 ) [0,1) [0,1) ∑ p i j = 1 \sum p_{ij} = 1 pij=1

  3. 选择下一个城市采用轮盘赌的方式,即产生一个在 [ 0 , 1 ) [0,1) [0,1) 随机数 r a n d rand rand 作为圆形转盘的指针指向的位置,利用 t m p tmp tmp p i j p_{ij} pij 累加和】锁定轮盘上的各个区域,一旦 t m p ≥ r a n d tmp \ge rand tmprand ,说明当前城市就是下一个目的地,将此城市在 v i s i t e d visited visited 中标记,同时将其加入访问顺序 t o u r tour tour 中。

    4e1eb5ff3c9d479796116fc341ac826e

    用此方法访问所有节点后,返回范围顺序 t o u r tour tour

    	// 蚂蚁移动
    	// pheromone : 信息素矩阵
    	private int[] antMove(double[][] pheromone) {
    		// 存储依次到达的城市序号
    		int[] tour = new int[cityNum];
    		boolean[] visited = new boolean[cityNum];
    		Random random = new Random();
    		// 生成一个不大于 cityNum 的伪随机整数
    		int start = random.nextInt(cityNum);
    		// 从 start 城市出发,此城市标记为已访问
    		visited[start] = true;
    		// 第一个访问 start 城市
    		tour[0] = start;
    		for (int i = 1; i < cityNum; i++) {
    			// 当前蚂蚁所在的城市
    			int curCity = tour[i - 1];
    			// p 存放概率公式的分子部分
    			double[] p = new double[cityNum];
    			// sum 存放概率公式的分母部分
    			double sum = 0.0;
    			for (int j = 0; j < cityNum; j++) {
    				// 城市 j 没有被访问过
    				if (!visited[j]) {
    					p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta);
    					sum += p[j];
    				}
    			}
    			// 用 [0,1] 范围的伪随机数代替轮盘针转到的位置
    			double rand = random.nextDouble()  ;
    			double tmp = 0.0;
    			int nextCity = 0;
    			for (int j = 0; j < cityNum; j++) {
    				if (!visited[j]) {
    					// 每个概率 p[j]/sum 代表轮盘上的一块区域
    					// tmp 累积值代表指针到的位置
    					tmp += p[j]/sum;
    					// 超过 rand ,说明来到了这个区间,即当前城市就是目标城市
    					if (tmp >= rand) {
    						nextCity = j;
    						break;
    					}
    				}
    			}
    			// 标记城市为已访问
    			visited[nextCity] = true;
    			// 记录蚂蚁的下个城市目的地
    			tour[i] = nextCity;
    		}
    		return tour;
    	}
    
  4. 由于知道城市访问顺序 t o u r tour tour ,因此不难得到访问总距离 t o u r L e n g t h tourLength tourLength ,如果这个总距离小于 b e s t T o u r bestTour bestTour ,说明这是更优的解法,因此更新 b e s t T o u r bestTour bestTour b e s t L e n g t h bestLength bestLength

  5. 蚂蚁访问完各个城市后开始更新各路径上的信息素增量 Δ τ \Delta\tau Δτ Δ τ \Delta\tau Δτ 由是信息素增加强度系数 Q Q Q,和 L L L 蚂蚁完成路径的长度决定

  6. 当所有蚂蚁都将城市访问完毕后,将信息素增量 Δ τ \Delta\tau Δτ 更新至原路径上的信息素中,同时还需要考虑信息素的挥发,最终得到第一次迭代后的信息素矩阵情况

    	// 更新信息素矩阵
    	private void updatePheromone() {
    		// 蚂蚁在经过路径上留下的信息增量
    		double[][] deltaPheromone = new double[cityNum][cityNum];
    		for (int i = 0; i < antNum; i++) {
    			// 单只蚂蚁根据当前信息素矩阵而得到的访问顺序
    			int[] tour = antMove(pheromone);
    			// 当前蚂蚁访问的长度
    			double tourLength = getTourLength(tour);
    			// 说明可以优化
    			if (tourLength < bestLength) {
    				// 更新最短路径
    				bestLength = tourLength;
    				// 由于有了最短路径,因此存储的访问路径也需要更新
    				System.arraycopy(tour, 0, bestTour, 0, cityNum);
    			}
    			for (int j = 0; j < cityNum - 1; j++) {
    				int city1 = tour[j];
    				int city2 = tour[j + 1];
    				// 信息素增量Δτ,只要有蚂蚁经过就会变多
    				deltaPheromone[city1][city2] += q / tourLength;
    				deltaPheromone[city2][city1] = deltaPheromone[city1][city2];
    			}
    			deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength;
    			deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]];
    		}
    		for (int i = 0; i < cityNum; i++) {
    			for (int j = 0; j < cityNum; j++) {
    				// 原来的信息素挥发了部分,同时新的信息素又增加了一些
    				pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j];
    			}
    		}
    	}
    
  7. 多次迭代后可以得到最优路径,存放在 b e s t T o u r bestTour bestTour 中,最短路径长度存放于 b e s t L e n g t h bestLength bestLength

    	// 运行蚁群算法
    	public void run() {
    		initPheromone();
    		// 迭代次数
    		for (int i = 0; i < maxGen; i++) {
    			updatePheromone();
    		}
    		System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength);
    		System.out.println("最优路径为:");
    		for (int i = 0; i < cityNum; i++) {
    			System.out.print(bestTour[i] + " ");
    		}
    	}
    
  8. 样例测试

    public class AntColonyOptimizationTest {
        public static void main(String[] args) {
            // 城市数目
            int cityNum = 4;
            // 蚂蚁数目
            int antNum = 20;
            // 迭代次数
            int maxGen = 100;
            // 信息素重要程度因子
            double alpha = 1.0;
            // 距离的重要程度因子
            double beta = 5.0;
            // 信息素挥发因子
            double rho = 0.5;
            // 信息素增加强度系数
            double q = 1;
    
            // 初始化距离矩阵【无向图】
            double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}};
            System.out.println("距离矩阵distance为:");
            for (double[] ds : distance) {
    			System.out.println(Arrays.toString(ds));
    		}
            AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q);
            aco.run();
        }
    }
    
    image-20230513181850250

4. 完整代码

4.1 AntColonyOptimization类

package aca;

import java.util.Random;
public class AntColonyOptimization {
	 // 距离矩阵
	private double[][] distance;
	// 信息素矩阵
	private double[][] pheromone; 
	// 城市数量
	private int cityNum; 
	// 蚂蚁数量
	private int antNum; 
	// 最大迭代次数
	private int maxGen; 
	// 信息素因子
	private double alpha; 
	// 距离重要程度因子
	private double beta; 
	// 信息素挥发因子
	private double rho; 
	// 信息素增加强度系数
	private double q; 
	// 最优路径
	private int[] bestTour; 
	// 最优路径长度
	private double bestLength; 

	public AntColonyOptimization(double[][] distance, int antNum, int maxGen, double alpha, double beta, double rho,
			double q) {
		this.distance = distance;
		this.cityNum = distance.length;
		this.antNum = antNum;
		this.maxGen = maxGen;
		this.alpha = alpha;
		this.beta = beta;
		this.rho = rho;
		this.q = q;
		this.pheromone = new double[cityNum][cityNum];
		this.bestTour = new int[cityNum];
		this.bestLength = Double.MAX_VALUE;
	}

	// 初始化信息素矩阵
	private void initPheromone() {
		// 信息素初始值
		double initPheromone = 1.0 / (cityNum * distance[0][1]);
		for (int i = 0; i < cityNum; i++) {
			for (int j = 0; j < cityNum; j++) {
				// 各条路径的信息素浓度
				pheromone[i][j] = initPheromone;
			}
		}
	}

	/**
	 * 
	 * @param pheromone 信息素矩阵
	 * @return 蚂蚁依次访问城市的顺序
	 */
	// 蚂蚁移动
	private int[] antMove(double[][] pheromone) {
		// 存储依次到达的城市序号
		int[] tour = new int[cityNum];
		boolean[] visited = new boolean[cityNum];
		Random random = new Random();
		// 生成一个不大于 cityNum 的伪随机整数
		int start = random.nextInt(cityNum);
		// 从 start 城市出发,此城市标记为已访问
		visited[start] = true;
		// 第一个访问 start 城市
		tour[0] = start;
		for (int i = 1; i < cityNum; i++) {
			// 当前蚂蚁所在的城市
			int curCity = tour[i - 1];
			// p 存放概率公式的分子部分
			double[] p = new double[cityNum];
			// sum 存放概率公式的分母部分
			double sum = 0.0;
			for (int j = 0; j < cityNum; j++) {
				// 城市 j 没有被访问过
				if (!visited[j]) {
					p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta);
					sum += p[j];
				}
			}
			// 用 [0,1] 范围的伪随机数代替轮盘针转到的位置
			double rand = random.nextDouble()  ;
			double tmp = 0.0;
			int nextCity = 0;
			for (int j = 0; j < cityNum; j++) {
				if (!visited[j]) {
					// 每个概率 p[j]/sum 代表轮盘上的一块区域
					// tmp 累积值代表指针到的位置
					tmp += p[j]/sum;
					// 超过 rand ,说明来到了这个区间,即当前城市就是目标城市
					if (tmp >= rand) {
						nextCity = j;
						break;
					}
				}
			}
			// 标记城市为已访问
			visited[nextCity] = true;
			// 记录蚂蚁的下个城市目的地
			tour[i] = nextCity;
		}
		return tour;
	}

	// 更新信息素矩阵
	private void updatePheromone() {
		// 蚂蚁在经过路径上留下的信息增量
		double[][] deltaPheromone = new double[cityNum][cityNum];
		for (int i = 0; i < antNum; i++) {
			// 单只蚂蚁根据当前信息素矩阵而得到的访问顺序
			int[] tour = antMove(pheromone);
			// 当前蚂蚁访问的长度
			double tourLength = getTourLength(tour);
			// 说明可以优化
			if (tourLength < bestLength) {
				// 更新最短路径
				bestLength = tourLength;
				// 由于有了最短路径,因此存储的访问路径也需要更新
				System.arraycopy(tour, 0, bestTour, 0, cityNum);
			}
			for (int j = 0; j < cityNum - 1; j++) {
				int city1 = tour[j];
				int city2 = tour[j + 1];
				// 信息素增量Δτ,只要有蚂蚁经过就会变多
				deltaPheromone[city1][city2] += q / tourLength;
				deltaPheromone[city2][city1] = deltaPheromone[city1][city2];
			}
			deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength;
			deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]];
		}
		for (int i = 0; i < cityNum; i++) {
			for (int j = 0; j < cityNum; j++) {
				// 原来的信息素挥发了部分,同时新的信息素又增加了一些
				pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j];
			}
		}
	}

	/**
	 * 
	 * @param tour 蚂蚁访问顺序
	 * @return 当前访问顺序对应的路径长度
	 */
	// 获取路径长度
	private double getTourLength(int[] tour) {
		double tourLength = 0.0;
		for (int i = 0; i < cityNum - 1; i++) {
			// 将路径中城市间的距离相加
			tourLength += distance[tour[i]][tour[i + 1]];
		}
		// 最后还要返回起点
		tourLength += distance[tour[cityNum - 1]][tour[0]];
		return tourLength;
	}

	// 运行蚁群算法
	public void run() {
		initPheromone();
		// 迭代次数
		for (int i = 0; i < maxGen; i++) {
			updatePheromone();
		}
		System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength);
		System.out.print("最优路径为:");
		for (int i = 0; i < cityNum; i++) {
			System.out.print(bestTour[i] + " ");
		}
	}
}

4.2 AntColonyOptimizationTest类

package aca;
import java.util.Arrays;

import aca.AntColonyOptimization;
public class AntColonyOptimizationTest {
    public static void main(String[] args) {
        // 城市数目
        int cityNum = 4;
        // 蚂蚁数目
        int antNum = 20;
        // 迭代次数
        int maxGen = 100;
        // 信息素重要程度因子
        double alpha = 1.0;
        // 距离的重要程度因子
        double beta = 5.0;
        // 信息素挥发因子
        double rho = 0.5;
        // 信息素增加强度系数
        double q = 1;

        // 初始化距离矩阵【无向图】
        double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}};
//        double[][] distance = new double[cityNum][cityNum];
//        for (int i = 0; i < cityNum; i++) {
//            for (int j = i; j < cityNum; j++) {
//                if (i == j) {
//                    distance[i][j] = 0.0;
//                } else {
//                    double d = Math.random() * 100;
//                    // 城市 i 到城市 j 的距离
//                    distance[i][j] = d;
//                    distance[j][i] = d;
//                }
//            }
//        }
        System.out.println("距离矩阵distance为:");
        for (double[] ds : distance) {
			System.out.println(Arrays.toString(ds));
		}
        AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q);
        aco.run();
    }
}
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值