赞
踩
p i , j k p^{k}_{i,j} pi,jk = [ τ i , j ( t ) ] α [ η i , j ( t ) ] β ∑ j ∈ a l l o w ( k ) [ τ i , j ( t ) ] α [ η i , j ( t ) ] β j ∈ a l l o w ( k ) \frac{[\tau^{}_{i,j}(t)]^{\alpha}[\eta^{}_{i,j}(t)]^{\beta}}{\sum_{j\in allow(k)}[\tau^{}_{i,j}(t)]^{\alpha}[\eta^{}_{i,j}(t)]^{\beta}}{j\in allow(k)} ∑j∈allow(k)[τi,j(t)]α[ηi,j(t)]β[τi,j(t)]α[ηi,j(t)]βj∈allow(k)
Δ τ i , j ( t ) {\Delta\tau^{}_{i,j}(t)} Δτi,j(t) = Q L k = \frac{Q}{L^{}_{k}} =LkQ
τ i , j ( t ) = ρ ∗ τ i , j ( t ) {\tau^{}_{i,j}(t)} = \rho*\tau^{}_{i,j}(t) τi,j(t)=ρ∗τi,j(t)( ρ \rho ρ为设置的挥发系数)
旅行商问题是给定一组城市,环绕此城市一圈,经过每一个城市,找到路径的最短距离。
此类问题在路径较小时可使用暴力解,但当城市数量较多时(本次采用ATT48的数据,共48个城市,解空间大小为48的阶乘),暴力解及传统的解法无法在较短的时间内计算出路径(普通计算机得连续工作几万年,反正那时谁还记得谁,另一句,几万年是我瞎BB的,就是时间很长就行了,不过也差不多是这个数),这显然不是我们想要的,而贪婪算法能在有限时间内求得一个较好的答案,但仍然离最优解相差甚远(误差值达到13%)。
蚁群算法本质上是一种启发式的优化算法,有分布计算、信息正反馈和启发式搜索的特征,能尽量避开局部最优,达到一个较好的解(根据我的实测,依然不如遗传算法,有时间再写个遗传算法)。
参数设置对于蚁群算法的性能有着极其重要的影响,不同的参数设置会导致结果的较大差异,一般情况下将不同常量设置如下可取得较好的结果:
α
{\alpha}
α(信息素浓度的影响因子):设置为1;
β
{\beta}
β(城市间距离的影响因子):设置为5;
ρ
{\rho}
ρ(挥发因子):设置为0.9。
通过这几天写各种算法,我深刻体会到将不同的计算包装成不同的函数,可以提高很大的效率,以下介绍几个具体的函数。
double rij = Math .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])) /10.0);
蚁群算法类(main方法所在类):
package aca; import java.awt.Image; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.stream.events.StartDocument; public class ACA { int ant_num=50;/*蚂蚁数量*/ int alpha =1;//α因子 int beta = 5;//β因子 static double rho =0.9;//信息素挥发因子 int Q=1;//常数 public double[][] init_p(int [][]distance) { //对信息素概率的初始量进行更新,初始概率为每个点到其余点的路径和分之路径 double p[][] = new double[48][48] ;//用于保存信息素概率 for(int i=0;i<48;i++) { double sum = for_each_sum(distance, i);//对每个结点进行和的计算 for(int j=0;j<48;j++) { p[i][j]=(distance[i][j]*1.0)/sum; } } for(int i=0;i<48;i++) { p[i][i]=0; } return p; } public int[] start(double [][]p,int[][]distance) { int length=0; int []allow = new int[48];//保存走过的距离 for(int m=0;m<48;m++) { allow[m]=100;//防止NOT_EXIT失效 } int i=0; int temple = (int) (Math.random()*48); allow[i]=temple; i++; int current =temple; while(i<48) { int tmp[]= new int[48]; for(int m=0;m<48;m++) { tmp[m]=allow[m]; //System.out.println(m); } int next = roulette(p, current, tmp,distance); allow[i]=next; current = next; i++; } return allow; } public int roulette(double [][]p,int current,int []allow,int[][]distance) { //轮盘赌,current为当前城市标号 double sum =0; int temple = 0; double a = sum_single_possible(distance, p, current,allow); while(sum<0.3) { temple = (int) (Math.random()*48); if(not_exit(temple, allow)) { //假如该数不在数组里,则令概率增加 double n =1.0/distance[current][temple]; n = Math.pow(n, beta); double t = p[current][temple]; t = Math.pow(t, alpha); sum =sum + n*t*1.0/a; } } return temple; } public double sum_single_possible(int [][]distance,double[][]p,int current,int[]allow) { //根据公式计算概率 double sum=0; for(int i=0;i<48;i++) { if(not_exit(i, allow)) { double a =1.0/distance[current][i]; a= Math.pow(a,beta); double b =p[current][i]; b= Math.pow(b, alpha); sum = sum+a*b; } } return sum; } public boolean not_exit(int current,int []allow) { //判断当前数是否已经存在于走过的路径中 boolean flag = true; for(int i=0;i<allow.length;i++) { if(current == allow[i]) return false; } return true; } public double for_each_sum(int [][]distance,int i) { //辅助函数,帮助计算每行总值,I表达第I个结点 double sum=0; for(int j = 0;j<48;j++) { sum = sum + distance[i][j]; } return sum; } public int sum_path_length(int [][]distance,int [][]path,int current) { //辅助函数,计算蚂蚁走过路径总值 int sum=0; for(int i=0;i<47;i++) { sum = sum + distance[path[current][i]][path[current][i+1]]; } sum = sum + distance[path[current][47]][path[current][0]]; return sum; } public static void main(String []args) throws IOException { bbtsp a = new bbtsp(); int [][]distance = a.init("c://data.txt"); ACA b =new ACA(); double p[][] =b.init_p(distance);//产生概率 int iter = 200; int i=1; int [][]path = new int[50][48]; int min = Integer.MAX_VALUE;//保存最佳路径 int best_path[]=new int[48];//保存最佳路径 while(i<100) { for(int j=0;j<50;j++)//令每个蚂蚁去寻找路径 { int[]allow = b.start(p,distance);//产生一个初始路径 for(int m=0;m<48;m++) {//将此值保存在路径数组中用于处理 path[j][m]=allow[m]; } } //释放信息素 for(int j=0;j<50;j++) { int current = j;//选中第J条路径 int L = b.sum_path_length(distance, path, current);//计算每条路径长度 if(L<min) { min = L;//更新最优解 for(int k=0;k<48;k++)//保存最短路径 { best_path[k]=path[current][k]; } } for(int k=0;k<47;k++) { //释放信息素的值 p[path[j][k]][path[j][k+1]]=p[path[j][k]][path[j][k+1]]+1.0/L; } p[path[j][47]][path[j][0]]=p[path[j][47]][path[j][0]]+1.0/L; } //挥发信息素 for(int j=0;j<48;j++) { for(int k=0;k<48;k++) { p[j][k]=rho*p[j][k]; } } i++; } System.out.println("最终路径:"); for(int k=0;k<48;k++) { System.out.print(best_path[k]+1+" "); } System.out.print("最小距离:"); System.out.println(min); /*int[]allow = b.start(p,distance); for(int i=0;i<48;i++) { for(int j=0;j<48;j++) { if(allow[i]>allow[j]) { int temp=allow[i]; allow[i]=allow[j]; allow[j]=temp; } } } for(int j = 0;j<48;j++) { System.out.println(allow[j]); }*/ } }
返回距离矩阵的类:
package aca; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; class bbtsp{ int [][]distance;//距离矩阵 int citynum = 48; public bbtsp(int [][]dis) { this.distance = dis; } public bbtsp() { } public int[][] init(String filename) throws IOException { // 读取数据 int[] x; int[] y; String strbuff; BufferedReader data = new BufferedReader(new InputStreamReader( new FileInputStream(filename))); distance= new int[citynum][citynum]; x = new int[citynum]; y = new int[citynum]; for (int i = 0; i < citynum; i++) { // 读取一行数据,数据格式1 6734 1453 strbuff = data.readLine(); // 字符分割 String[] strcol = strbuff.split(" "); x[i] = Integer.valueOf(strcol[1]);// x坐标 y[i] = Integer.valueOf(strcol[2]);// y坐标 } data.close(); // 计算距离矩阵 // ,针对具体问题,距离计算方法也不一样,此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628 for (int i = 0; i < citynum - 1; i++) { distance[i][i] = -1; // 对角线为0 for (int j = i + 1; j < citynum; j++) { double rij = Math .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])) /10.0); // 四舍五入,取整 int tij = (int) Math.round(rij); if (tij < rij) { distance[i][j] = tij + 1; distance[j][i] = distance[i][j]; } else { distance[i][j] = tij; distance[j][i] = distance[i][j]; } } } distance[citynum - 1][citynum - 1] = -1; return distance; } }
这是我得到的11011的数据:11011
5 48 42 10 24 45 35 4 26 2 29 41 16 22 3 34
14 25 13 23 11 12 15 40 9 1 8 38 31 44 18 7
28 36 6 37 19 27 43 17 30 46 33 20 47 21
32 39
最优解为10628,误差值为0.03%,显然这个误差值是比较大的,但相对于贪婪算法13%的误差,这个值还能接受
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。