记一次蚁群算法解决TSP问题

算法规则

1)范围
蚂蚁观察到的范围是一个方格世界,蚂蚁有一个参数为速度半径(一般是3),那么它能观察到的范围就是3*3个方格世界,并且能移动的距离也在这个范围之内。

2)折叠环境
蚂蚁所在的环境是一个虚拟的世界,其中有障碍物,有别的蚂蚁,还有信息素,信息素有两种,一种是找到食物的蚂蚁洒下的食物信息素,一种是找到窝的蚂蚁洒下的窝的信息素。每个蚂蚁都仅仅能感知它范围内的环境信息。环境以一定的速率让信息素消失。

3)折叠觅食规则
在每只蚂蚁能感知的范围内寻找是否有食物,如果有就直接过去。否则看是否有信息素,并且比较在能感知的范围内哪一点的信息素最多,这样,它就朝信息素多的地方走,并且每只蚂蚁都会以小概率犯错误,从而并不是往信息素最多的点移动。蚂蚁找窝的规则和上面一样,只不过它对窝的信息素做出反应,而对食物信息素没反应。

4)折叠移动规则
每只蚂蚁都朝向信息素最多的方向移,并且,当周围没有信息素指引的时候,蚂蚁会按照自己原来运动的方向惯性的运动下去,并且,在运动的方向有一个随机的小的扰动。为了防止蚂蚁原地转圈,它会记住刚才走过了哪些点,如果发现要走的下一点已经在之前走过了,它就会尽量避开。

5)折叠避障规则
如果蚂蚁要移动的方向有障碍物挡住,它会随机的选择另一个方向,并且有信息素指引的话,它会按照觅食的规则行为。

6)折叠信息素规则
每只蚂蚁在刚找到食物或者窝的时候撒发的信息素最多,并随着它走远的距离,播撒的信息素越来越少。

根据这几条规则,蚂蚁之间并没有直接的关系,但是每只蚂蚁都和环境发生交互,而通过信息素这个纽带,实际上把各个蚂蚁之间关联起来了。比如,当一只蚂蚁找到了食物,它并没有直接告诉其它蚂蚁这儿有食物,而是向环境播撒信息素,当其它的蚂蚁经过它附近的时候,就会感觉到信息素的存在,进而根据信息素的指引找到了食物。

算法实现

蚂蚁类

属性:
蚂蚁的视野
int sightdistance = 2000;
蚂蚁的路径
public int[]tour
记录蚂蚁走过的城市

    /** * 表示城市的数组,若访问过,则为0,否则为1 */
    int[] unvisitedcity;

方法:
1.设置蚂蚁初始出现的城市
public void RandomSelectCity(int citycount)
2.蚂蚁下一个出现的城市(重点)
public void SelectNextCity(int index,double[][]tao,int[][]distance)
其中index意为下一个走过的是第index城市,tao为信息素地图,distance为距离地图。
主要思路:
1)查找蚂蚁周围是否有信息素,若有信息素转至3),否则转到2)
2)蚂蚁因为惯性,选择离自己最近的城市为下一个地点,转到5)
3)蚂蚁综合路径的长度和信息素的代价
4)由轮盘赌算法,选择下一个城市
5)洒下自己的信息素

蚁群算法类的实现

属性:

    ant []ants;//蚂蚁群
    int antcount;//蚂蚁的数量
    int [][]distance;//表示城市间距离
    double [][]tao;//信息素矩阵
    int citycount;//城市数量
    int[]besttour;//求解的最佳路径
    int bestlength;//求的最优解的长度

方法:
1.初始化蚁群和TSP问题的地图
public void init(String filename,int antnum)
filename为TSP问题文件,antnum为蚂蚁群的蚂蚁
2.算法的进行
public void run(int maxgen)
maxgen为迭代的次数

主要思路:
1)将蚁群投放到每个城市中
2)让每一个蚂蚁选择自己要前往的下一个城市
3)更新信息素,老的信息素挥发,新的信息素洒下,若走完所有城市转向4),否则转向2)
4)计算每只蚂蚁走过的路程
5)将路程与最近路程比较,若更近,则替换
6)返回2)继续迭代,直到迭代完成

参数

蚂蚁数量:50
迭代次数:4000
蚂蚁的视野:2000
每次洒下信息素:2.0/蚂蚁走过的长度 (走的越远,信息素越少)
信息素挥发:每次变为原来的0.5倍

结果

路径: 46->19->32->45->6->27->5->36->18->26->16->42->29->35->17->43->30->37->7->0->8->39->14->11->10->22->2->21->15->40->33->28->1->25->3->34->44->23->9->41->4->47->31->38->24->13->12->20->46
最优路径长度是33900

数据

./data.txt

48
1 6734 1453
2 2233 10
3 5530 1424
4 401 841
5 3082 1644
6 7608 4458
7 7573 3716
8 7265 1268
9 6898 1885
10 1112 2049
11 5468 2606
12 5989 2873
13 4706 2674
14 4612 2035
15 6347 2683
16 6107 669
17 7611 5184
18 7462 3590
19 7732 4723
20 5900 3561
21 4483 3369
22 6101 1110
23 5199 2182
24 1633 2809
25 4307 2322
26 675 1006
27 7555 4819
28 7541 3981
29 3177 756
30 7352 4506
31 7545 2801
32 3245 3305
33 6426 3173
34 4608 1198
35 23 2216
36 7248 3779
37 7762 4595
38 7392 2244
39 3484 2829
40 6271 2135
41 4985 140
42 1916 1569
43 7280 4899
44 7509 3239
45 10 2676
46 6807 2993
47 5185 3258
48 3023 1942

源码

./ant.java

import java.util.Random;
/** * 蚂蚁类 * @author ZKY * */
public class ant {
    /** * 蚂蚁获得的路径 */
    public int[]tour;
    /** * 表示城市的数组,若访问过,则为0,否则为1 */
    int[] unvisitedcity;
    /** * 蚂蚁获得的路径长度 */
    public int tourlength;
    /** * 城市数 */
    int citys;
    /** * 蚂蚁的视野 */
    int sightdistance = 2000;
    int startcity;
/** * 随机分配蚂蚁到某个城市中 * 同时完成蚂蚁包含字段的初始化工作 * @param citycount 总的城市数量 */
    public void RandomSelectCity(int citycount){
    	sightdistance = 2000;
        citys=citycount;
        unvisitedcity=new int[citycount];
        tour=new int[citycount+1];
        tourlength=0;
   
        for(int i=0;i<citycount;i++){
        	//初始化路径和未访问城市
            tour[i]=-1;
            unvisitedcity[i]=1;
        }
        long r1 = System.currentTimeMillis();
        Random rnd=new Random(r1);
        int firstcity=rnd.nextInt(citycount);
        unvisitedcity[firstcity]=0;
        tour[0]=firstcity;
        startcity = firstcity;
    }
    /** * 选择下一个城市 * @param index 需要选择第index个城市了 * @param tao 全局的信息素信息 * @param distance 全局的距离矩阵信息 */
    public void SelectNextCity(int index,double[][]tao,int[][]distance){
        double []p;
        p=new double[citys];
        double alpha=1.0;
        double beta=2.0;
        double sum=0;
        int currentcity=tour[index-1];
        //若视野范围没有信息素,则hastao为0
        int hastao = 0;
        //计算公式中的分母部分
        for(int i=0;i<citys;i++){
        	
            if(unvisitedcity[i]==1 && distance[currentcity][i] <= sightdistance) {
                sum+=(Math.pow(tao[currentcity][i], alpha)*
                        Math.pow(1.0/distance[currentcity][i], beta));
            	hastao++;
            }
        }
        if (hastao == 0) {//若视野中没有信息素
        	int mindistance = -1;
    		int mincity = -1;
        	for (int i=0;i<citys;i++) {
        		if(unvisitedcity[i]==1) {
        			if (mindistance == -1) {
        				mindistance = distance[currentcity][i];
        				mincity = i;
        			}
        			if (distance[currentcity][i] < mindistance) {
        				mindistance = distance[currentcity][i];
        				mincity = i;
        			}
        		}
        	}
        	if (mincity == -1) 
        		mincity = startcity;
        	//蚂蚁因为惯性,向最近的城市前进
        	//System.out.println("#"+mincity);jiang
        	tour[index] = mincity;
        	unvisitedcity[mincity]=0;
        	return;
        	
        }
        
        //计算每个城市被选中的概率
        for(int i=0;i<citys;i++){
            if(unvisitedcity[i]==0 || distance[currentcity][i] > sightdistance)
                p[i]=0.0;
            else{
                p[i]=(Math.pow(tao[currentcity][i], alpha)*
                        Math.pow(1.0/distance[currentcity][i], beta))/sum;
                
            }
        }
        long r1 = System.currentTimeMillis();
        Random rnd=new Random(r1);
        double selectp=rnd.nextDouble();
        selectp = Math.random();   
        //轮盘赌选择一个城市;
        double sumselect=0;
        int selectcity=-1;
        for(int i=0;i<citys;i++){
            sumselect+=p[i];
            if(sumselect>=selectp){
                selectcity=i;
                break;
            }
        }
        if (selectcity==-1)
        	System.out.println("Error");;
        tour[index]=selectcity;
        unvisitedcity[selectcity]=0;
    }
    /** * 计算蚂蚁获得的路径的长度 * @param distance 全局的距离矩阵信息 */
    public void CalTourLength(int [][]distance){
        tourlength=0;
        tour[citys]=tour[0];
        for(int i=0;i<citys;i++){
            tourlength+=distance[tour[i]][tour[i+1]];
        }    
    }
}

./ACO.java

package tspsolver;
import java.io.*;
/** *蚁群优化算法,用来求解TSP问题 * @author ZKY */
public class ACO {
    //定义蚂蚁群
    ant []ants;//蚂蚁群
    int antcount;//蚂蚁的数量
    int [][]distance;//表示城市间距离
    double [][]tao;//信息素矩阵
    int citycount;//城市数量
    int[]besttour;//求解的最佳路径
    int bestlength;//求的最优解的长度
    /** 初始化函数 *@param filename tsp数据文件 *@param antnum 系统用到蚂蚁的数量 *@throws 如果文件不存在则抛出异常 */
    public void init(String filename,int antnum) throws FileNotFoundException, IOException{
        antcount=antnum;
        ants=new ant[antcount];
        //读取数据
        int[] x;
        int[] y;
        String strbuff;
        BufferedReader tspdata = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
        strbuff = tspdata.readLine();
        citycount = Integer.valueOf(strbuff);
        distance = new int[citycount][citycount];
        x = new int[citycount];
        y = new int[citycount];
        for (int citys = 0; citys < citycount; citys++) {
            strbuff = tspdata.readLine();
            String[] strcol = strbuff.split(" ");
            x[citys] = Integer.valueOf(strcol[1]);
            y[citys] = Integer.valueOf(strcol[2]);
        }
        //计算距离矩阵
        for (int city1 = 0; city1 < citycount - 1; city1++) {
            distance[city1][city1] = 0;
            for (int city2 = city1 + 1; city2 < citycount; city2++) {
                distance[city1][city2] = (int) (Math.sqrt((x[city1] - x[city2]) * (x[city1] - x[city2])
                        + (y[city1] - y[city2]) * (y[city1] - y[city2])) + 0.5);
                distance[city2][city1] = distance[city1][city2];
            }
        }
        distance[citycount - 1][citycount - 1] = 0;
        
        //初始化信息素矩阵
        tao=new double[citycount][citycount];
        for(int i=0;i<citycount;i++)
        {
            for(int j=0;j<citycount;j++){
                tao[i][j]=0.1;
            }
        }
        bestlength=Integer.MAX_VALUE;
        besttour=new int[citycount+1];
        //随机放置蚂蚁
        for(int i=0;i<antcount;i++){
            ants[i]=new ant();
            ants[i].RandomSelectCity(citycount);
        }
        tspdata.close();
    }
    /** * ACO的运行过程 * @param maxgen ACO的最多循环次数 * */
    public void run(int maxgen){
        for(int runtimes=0;runtimes<maxgen;runtimes++){
            //每一只蚂蚁移动的过程
            for(int i=0;i<antcount;i++){
                for(int j=1;j<citycount;j++){
                    ants[i].SelectNextCity(j,tao,distance);
                }
                //计算蚂蚁获得的路径长度
                ants[i].CalTourLength(distance);
                if(ants[i].tourlength<bestlength){
                    //保留最优路径
                    bestlength=ants[i].tourlength;
                    System.out.println("第"+runtimes+"代,发现新的解"+bestlength);
                    for(int j=0;j<citycount+1;j++) {
                        besttour[j]=ants[i].tour[j];
                    	System.out.print(besttour[j]+ "->");
                    }
                    System.out.println();
                }
            }
            //更新信息素矩阵
            UpdateTao();
            //重新随机设置蚂蚁
            for(int i=0;i<antcount;i++){
                ants[i].RandomSelectCity(citycount);
            }
        }
       }
    /** * 更新信息素矩阵 */
    private void UpdateTao(){
        double rou=0.5;
        //信息素挥发
        for(int i=0;i<citycount;i++)
            for(int j=0;j<citycount;j++)
                tao[i][j]=tao[i][j]*(1-rou);
        //信息素更新
        for(int i=0;i<antcount;i++){
            for(int j=0;j<citycount;j++){
                tao[ants[i].tour[j]][ants[i].tour[j+1]]+=2.0/ants[i].tourlength;
                tao[ants[i].tour[j+1]][ants[i].tour[j]]+=2.0/ants[i].tourlength;
            }
        }
    }
    /** * 输出程序运行结果 */
    public void ReportResult(){
        System.out.println("最优路径长度是"+bestlength);
    }
}

./Main.java

package tspsolver;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/** *主程序 调用ACO求解问题 * @author ZKY */
public class Main {
    /** * @param args the command line arguments */
    public static void main(String[] args) {
        // TODO code application logic here
        ACO aco;
        aco=new ACO();
        try {
            aco.init("D://data.txt", 50);
            aco.run(4000);
            aco.ReportResult();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
    原文作者:蚁群算法
    原文地址: https://blog.csdn.net/zhuochuyu7096/article/details/85538541
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞