当前位置:   article > 正文

蚁群算法java实现以及TSP问题蚁群算法求解_publicant

publicant

1. 蚁群算法简介

     蚁群算法(Ant Clony Optimization, ACO)是一种群智能算法,它是由一群无智能或有轻微智能的个体(Agent)通过相互协作而表现出智能行为,从而为求解复杂问题提供了一个新的可能性。蚁群算法最早是由意大利学者Colorni A., Dorigo M. 等于1991年提出。经过20多年的发展,蚁群算法在理论以及应用研究上已经得到巨大的进步。

      蚁群算法是一种仿生学算法,是由自然界中蚂蚁觅食的行为而启发的。在自然界中,蚂蚁觅食过程中,蚁群总能够按照寻找到一条从蚁巢和食物源的最优路径。图(1)显示了这样一个觅食的过程。


图(1)蚂蚁觅食

 

     在图1(a)中,有一群蚂蚁,假如A是蚁巢,E是食物源(反之亦然)。这群蚂蚁将沿着蚁巢和食物源之间的直线路径行驶。假如在A和E之间突然出现了一个障碍物(图1(b)),那么,在B点(或D点)的蚂蚁将要做出决策,到底是向左行驶还是向右行驶?由于一开始路上没有前面蚂蚁留下的信息素(pheromone),蚂蚁朝着两个方向行进的概率是相等的。但是当有蚂蚁走过时,它将会在它行进的路上释放出信息素,并且这种信息素会议一定的速率散发掉。信息素是蚂蚁之间交流的工具之一。它后面的蚂蚁通过路上信息素的浓度,做出决策,往左还是往右。很明显,沿着短边的的路径上信息素将会越来越浓(图1(c)),从而吸引了越来越多的蚂蚁沿着这条路径行驶。

2. TSP问题描述

      蚁群算法最早用来求解TSP问题,并且表现出了很大的优越性,因为它分布式特性,鲁棒性强并且容易与其它算法结合,但是同时也存在这收敛速度慢,容易陷入局部最优(local optimal)等缺点。

      TSP问题(Travel Salesperson Problem,即旅行商问题或者称为中国邮递员问题),是一种,是一种NP-hard问题,此类问题用一般的算法是很大得到最优解的,所以一般需要借助一些启发式算法求解,例如遗传算法(GA),蚁群算法(ACO),微粒群算法(PSO)等等。

      TSP问题可以分为两类,一类是对称TSP问题(Symmetric TSP),另一类是非对称问题(Asymmetric TSP)。所有的TSP问题都可以用一个图(Graph)来描述:

V={c1, c2, …, ci, …, cn},i = 1,2, …, n,是所有城市的集合.ci表示第i个城市,n为城市的数目;

E={(r, s): r,s∈ V}是所有城市之间连接的集合;

C = {crs: r,s∈ V}是所有城市之间连接的成本度量(一般为城市之间的距离);

如果crs = csr, 那么该TSP问题为对称的,否则为非对称的。

一个TSP问题可以表达为:

求解遍历图G = (V, E, C),所有的节点一次并且回到起始节点,使得连接这些节点的路径成本最低。

3. 蚁群算法原理

      假如蚁群中所有蚂蚁的数量为m,所有城市之间的信息素用矩阵pheromone表示,最短路径为bestLength,最佳路径为bestTour。每只蚂蚁都有自己的内存,内存中用一个禁忌表(Tabu)来存储该蚂蚁已经访问过的城市,表示其在以后的搜索中将不能访问这些城市;还有用另外一个允许访问的城市表(Allowed)来存储它还可以访问的城市;另外还用一个矩阵(Delta)来存储它在一个循环(或者迭代)中给所经过的路径释放的信息素;还有另外一些数据,例如一些控制参数(clip_image002[4]clip_image002[6]clip_image002[8]Q),该蚂蚁行走玩全程的总成本或距离(tourLength),等等。假定算法总共运行MAX_GEN次,运行时间为t

蚁群算法计算过程如下:

(1)初始化

t=0,初始化bestLength为一个非常大的数(正无穷),bestTour为空。初始化所有的蚂蚁的Delt矩阵所有元素初始化为0,Tabu表清空,Allowed表中加入所有的城市节点。随机选择它们的起始位置(也可以人工指定)。在Tabu中加入起始节点,Allowed中去掉该起始节点。

(2)为每只蚂蚁选择下一个节点。

为每只蚂蚁选择下一个节点,该节点只能从Allowed中以某种概率(公式1)搜索到,每搜到一个,就将该节点加入到Tabu中,并且从Allowed中删除该节点。该过程重复n-1次,直到所有的城市都遍历过一次。遍历完所有节点后,将起始节点加入到Tabu中。此时Tabu表元素数量为n+1(n为城市数量),Allowed元素数量为0。接下来按照(公式2)计算每个蚂蚁的Delta矩阵值。最后计算最佳路径,比较每个蚂蚁的路径成本,然后和bestLength比较,若它的路径成本比bestLength小,则将该值赋予bestLength,并且将其Tabu赋予BestTour。

(公式1)

(公式2)

其中clip_image002[12]表示选择城市j的概率,k表示第k个蚂蚁,clip_image002[10]表示城市ij在第t时刻的信息素浓度,clip_image002[16]表示从城市i到城市j的可见度,

clip_image002[18]clip_image002[20]表示城市ij之间的成本(或距离)。由此可见clip_image002[20]越小,clip_image002[16]越大,也就是从城市ij的可见性就越大。clip_image002[26]表示蚂蚁k在城市ij之间留下的信息素。

clip_image002[28]表示蚂蚁k经过一个循环(或迭代)锁经过路径的总成本(或距离),即tourLength.clip_image002[4]clip_image002[6]Q均为控制参数。

(3)更新信息素矩阵

t = t + n,按照(公式3)更新信息素矩阵phermone。

(公式3)

clip_image002[32]t+n时刻城市ij之间的信息素浓度。clip_image002[8]为控制参数,clip_image002[35]为城市ij之间信息素经过一个迭代后的增量。并且有

(公式4)

其中clip_image002[26]由公式计算得到。

(4)检查终止条件

如果达到最大代数MAX_GEN,算法终止,转到第(5)步;否则,重新初始化所有的蚂蚁的Delt矩阵所有元素初始化为0,Tabu表清空,Allowed表中加入所有的城市节点。随机选择它们的起始位置(也可以人工指定)。在Tabu中加入起始节点,Allowed中去掉该起始节点,重复执行(2),(3),(4)步。

(5)输出最优值

4. Java实现

      在该java实现中我们选择使用tsplib上的数据att48,这是一个对称tsp问题,城市规模为48,其最优值为10628.其距离计算方法如图(2)所示:


图(2)att48距离计算方法

      实现中,使用了两个java类,一个Ant类,一个ACO类。(在转载的前提下做了一点点个人的修改)

具体实现代码如下(此代码借鉴了蚁群优化算法的JAVA实现):


  1. package tsp;
  2. import java.util.Random;
  3. import java.util.Vector;
  4. /**
  5. *
  6. * @author ychXu
  7. *
  8. */
  9. public class Ant implements Cloneable
  10. {
  11. /**
  12. * 已搜索过的城市
  13. */
  14. private Vector<Integer> tabu; // 已搜索过的城市
  15. /**
  16. * 尚未搜索的城市
  17. */
  18. private Vector<Integer> allowedCities; // 尚未搜索的城市
  19. /**
  20. * 信息素变化矩阵
  21. */
  22. private double[][] delta; // 信息素变化矩阵
  23. /**
  24. * 城市间距离矩阵
  25. */
  26. private int[][] distance; // 城市间距离矩阵
  27. /**
  28. * 公式常量
  29. */
  30. private double alpha; // 公式常量
  31. /**
  32. * 公式常量
  33. */
  34. private double beta; // 公式常量
  35. /**
  36. * 路径长度
  37. */
  38. private int tourLength; // 路径长度
  39. /**
  40. * 城市数量
  41. */
  42. private int cityNum; // 城市数量
  43. /**
  44. * 起始城市
  45. */
  46. private int firstCity; // 起始城市
  47. /**
  48. * 当前城市
  49. */
  50. private int currentCity; // 当前城市
  51. /**
  52. * Constructor of Ant
  53. */
  54. public Ant()
  55. {
  56. cityNum = 30;
  57. tourLength = 0;
  58. }
  59. /**
  60. * Constructor of Ant
  61. *
  62. * @param num
  63. * 蚂蚁数量
  64. */
  65. public Ant(int num)
  66. {
  67. cityNum = num;
  68. tourLength = 0;
  69. }
  70. /**
  71. * 初始化蚂蚁,随机选择起始位置
  72. *
  73. * @param distance
  74. * 距离矩阵
  75. * @param a
  76. * alpha
  77. * @param b
  78. * beta
  79. */
  80. public void init(int[][] distance, double a, double b)
  81. {
  82. alpha = a;
  83. beta = b;
  84. allowedCities = new Vector<Integer>();
  85. tabu = new Vector<Integer>();
  86. this.distance = distance;
  87. delta = new double[cityNum][cityNum];
  88. // 初始化数据成员
  89. for (int i = 0; i < cityNum; i++)
  90. {
  91. Integer integer = new Integer(i);
  92. allowedCities.add(integer);
  93. for (int j = 0; j < cityNum; j++)
  94. {
  95. delta[i][j] = 0.0;
  96. }
  97. }
  98. // 随机选择第一个城市位置
  99. Random random = new Random(System.currentTimeMillis());
  100. firstCity = random.nextInt(cityNum);
  101. for (Integer i : allowedCities)
  102. {
  103. if (i.intValue() == firstCity)
  104. {
  105. allowedCities.remove(i);
  106. break;
  107. }
  108. }
  109. tabu.add(Integer.valueOf(firstCity));
  110. currentCity = firstCity;
  111. }
  112. /**
  113. * 选择下一个城市,根据信息素
  114. *
  115. * @param pheromone
  116. * 信息素矩阵
  117. */
  118. public void selectNextCity(double[][] pheromone)
  119. {
  120. double[] p = new double[cityNum];
  121. double sum = 0;
  122. // 计算分母部分
  123. for (Integer i : allowedCities)
  124. {
  125. sum += Math.pow(pheromone[currentCity][i.intValue()], alpha)
  126. * Math.pow(1.0 / distance[currentCity][i.intValue()], beta);
  127. }
  128. // 计算概率矩阵
  129. for (int i = 0; i < cityNum; i++)
  130. {
  131. boolean flag = false;
  132. for (Integer j : allowedCities)
  133. {
  134. if (i == j.intValue())
  135. {
  136. p[i] = (Math.pow(pheromone[currentCity][i], alpha) * Math
  137. .pow(1.0 / distance[currentCity][i], beta))
  138. / sum;
  139. flag = true;
  140. break;
  141. }
  142. }
  143. if (flag == false)
  144. {
  145. p[i] = 0.0;
  146. }
  147. }
  148. // 采用轮盘赌选择下一个城市
  149. Random random = new Random(System.currentTimeMillis());
  150. double sleectP = random.nextDouble();
  151. int selectCity = 0;
  152. double sum1 = 0.f;
  153. for (int i = 0; i < cityNum; i++)
  154. {
  155. sum1 += p[i];
  156. if (sum1 >= sleectP)
  157. {
  158. selectCity = i;
  159. break;
  160. }
  161. }
  162. // 从允许选择的城市中去除select city
  163. for (Integer i : allowedCities)
  164. {
  165. if (i.intValue() == selectCity)
  166. {
  167. allowedCities.remove(i);
  168. break;
  169. }
  170. }
  171. // 在已搜索过的城市表中添加select city
  172. tabu.add(Integer.valueOf(selectCity));
  173. // 将当前城市改为选择的城市
  174. currentCity = selectCity;
  175. }
  176. /**
  177. * 计算当前蚂蚁路径长度
  178. *
  179. * @return 路径长度
  180. */
  181. private int calculateTourLength()
  182. {
  183. int len = 0;
  184. for (int i = 0; i < cityNum; i++)
  185. {
  186. len += distance[this.tabu.get(i).intValue()][this.tabu.get(i + 1)
  187. .intValue()];
  188. }
  189. return len;
  190. }
  191. public Vector<Integer> getAllowedCities()
  192. {
  193. return allowedCities;
  194. }
  195. public void setAllowedCities(Vector<Integer> allowedCities)
  196. {
  197. this.allowedCities = allowedCities;
  198. }
  199. public int getTourLength()
  200. {
  201. tourLength = calculateTourLength();
  202. return tourLength;
  203. }
  204. public void setTourLength(int tourLength)
  205. {
  206. this.tourLength = tourLength;
  207. }
  208. public int getCityNum()
  209. {
  210. return cityNum;
  211. }
  212. public void setCityNum(int cityNum)
  213. {
  214. this.cityNum = cityNum;
  215. }
  216. public Vector<Integer> getTabu()
  217. {
  218. return tabu;
  219. }
  220. public void setTabu(Vector<Integer> tabu)
  221. {
  222. this.tabu = tabu;
  223. }
  224. public double[][] getDelta()
  225. {
  226. return delta;
  227. }
  228. public void setDelta(double[][] delta)
  229. {
  230. this.delta = delta;
  231. }
  232. public int getFirstCity()
  233. {
  234. return firstCity;
  235. }
  236. public void setFirstCity(int firstCity)
  237. {
  238. this.firstCity = firstCity;
  239. }
  240. }


  1. package tsp;
  2. import java.io.BufferedReader;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.io.InputStreamReader;
  7. /**
  8. *
  9. * @author ychXu
  10. *
  11. */
  12. public class ACO
  13. {
  14. /**
  15. * 蚁群
  16. */
  17. private Ant[] ants; // 蚂蚁
  18. /**
  19. * 蚂蚁数量
  20. */
  21. private int antNum; // 蚂蚁数量
  22. /**
  23. * 城市数量
  24. */
  25. private int cityNum; // 城市数量
  26. /**
  27. * 迭代次数
  28. */
  29. private int MAX_GEN; // 迭代次数
  30. /**
  31. * 信息素矩阵
  32. */
  33. private double[][] pheromone; // 信息素矩阵
  34. /**
  35. * 距离矩阵
  36. */
  37. private int[][] distance; // 距离矩阵
  38. /**
  39. * 最佳长度
  40. */
  41. private int bestLength; // 最佳长度
  42. /**
  43. * 最佳路径
  44. */
  45. private int[] bestTour; // 最佳路径
  46. private double alpha;
  47. private double beta;
  48. /**
  49. * 信息素蒸发率
  50. */
  51. private double rho;
  52. public ACO()
  53. {
  54. }
  55. /**
  56. * constructor of ACO
  57. *
  58. * @param n
  59. * 城市数量
  60. * @param m
  61. * 蚂蚁数量
  62. * @param g
  63. * 运行代数
  64. * @param a
  65. * alpha
  66. * @param b
  67. * beta
  68. * @param r
  69. * rho
  70. *
  71. **/
  72. public ACO(int n, int m, int g, double a, double b, double r)
  73. {
  74. cityNum = n;
  75. antNum = m;
  76. ants = new Ant[antNum];
  77. MAX_GEN = g;
  78. alpha = a;
  79. beta = b;
  80. rho = r;
  81. }
  82. /**
  83. * 初始化ACO算法类
  84. *
  85. * @param filename
  86. * 数据文件名,该文件存储所有城市节点坐标数据
  87. * @throws IOException
  88. */
  89. public void init(String filename) throws FileNotFoundException, IOException
  90. {
  91. // 读取数据
  92. int[] x = new int[cityNum];
  93. int[] y = new int[cityNum];
  94. String strbuff;
  95. BufferedReader data = new BufferedReader(new InputStreamReader(
  96. new FileInputStream(filename)));
  97. distance = new int[cityNum][cityNum];
  98. for (int i = 0; i < cityNum; i++)
  99. {
  100. strbuff = data.readLine();
  101. String[] strcol = strbuff.split(" ");
  102. x[i] = Integer.valueOf(strcol[1]).intValue();
  103. y[i] = Integer.valueOf(strcol[2]).intValue();
  104. }
  105. // 计算距离矩阵
  106. // 针对具体问题,距离计算方法也不一样,此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628
  107. for (int i = 0; i < cityNum - 1; i++)
  108. {
  109. distance[i][i] = 0; // 对角线为0
  110. for (int j = i + 1; j < cityNum; j++)
  111. {
  112. double rij = Math
  113. .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
  114. * (y[i] - y[j])) / 10.0);
  115. int tij = (int) Math.round(rij);
  116. if (tij < rij)
  117. {
  118. distance[i][j] = tij + 1;
  119. distance[j][i] = distance[i][j];
  120. }
  121. else
  122. {
  123. distance[i][j] = tij;
  124. distance[j][i] = distance[i][j];
  125. }
  126. }
  127. }
  128. distance[cityNum - 1][cityNum - 1] = 0;
  129. // 初始化信息素矩阵
  130. pheromone = new double[cityNum][cityNum];
  131. for (int i = 0; i < cityNum; i++)
  132. {
  133. for (int j = 0; j < cityNum; j++)
  134. {
  135. pheromone[i][j] = 0.1; // 初始化为0.1
  136. }
  137. }
  138. bestLength = Integer.MAX_VALUE;
  139. bestTour = new int[cityNum + 1];
  140. // 随机放置蚂蚁
  141. for (int i = 0; i < antNum; i++)
  142. {
  143. ants[i] = new Ant(cityNum);
  144. ants[i].init(distance, alpha, beta);
  145. }
  146. }
  147. public void solve()
  148. {
  149. for (int g = 0; g < MAX_GEN; g++)
  150. {
  151. // 每一只蚂蚁移动的过程
  152. for (int i = 0; i < antNum; i++)
  153. {
  154. for (int j = 1; j < cityNum; j++)
  155. {
  156. ants[i].selectNextCity(pheromone);
  157. }
  158. // 蚂蚁回到起始位置FirstCity
  159. ants[i].getTabu().add(ants[i].getFirstCity());
  160. // 计算蚂蚁路径的长度
  161. if (ants[i].getTourLength() < bestLength)
  162. {
  163. bestLength = ants[i].getTourLength();
  164. System.out.println("第" + g + "迭代,发现新的解" + bestLength);
  165. for (int k = 0; k < cityNum + 1; k++)
  166. {
  167. bestTour[k] = ants[i].getTabu().get(k).intValue();
  168. System.out.print(bestTour[k] + " ");
  169. }
  170. System.out.println();
  171. }
  172. for (int j = 0; j < cityNum; j++)
  173. {
  174. ants[i].getDelta()[ants[i].getTabu().get(j).intValue()][ants[i]
  175. .getTabu().get(j + 1).intValue()] = 1.0 / ants[i]
  176. .getTourLength();
  177. ants[i].getDelta()[ants[i].getTabu().get(j + 1).intValue()][ants[i]
  178. .getTabu().get(j).intValue()] = 1.0 / ants[i]
  179. .getTourLength();
  180. }
  181. }
  182. // 更新信息素
  183. updatePheromone();
  184. // 重新初始化蚂蚁
  185. for (int i = 0; i < antNum; i++)
  186. {
  187. ants[i].init(distance, alpha, beta);
  188. }
  189. }
  190. System.out.println("\n迭代完毕。");
  191. // 打印最佳结果
  192. printOptimal();
  193. }
  194. // 更新信息素
  195. private void updatePheromone()
  196. {
  197. // 信息素挥发
  198. for (int i = 0; i < cityNum; i++)
  199. for (int j = 0; j < cityNum; j++)
  200. pheromone[i][j] = pheromone[i][j] * (1 - rho);
  201. // 信息素更新
  202. for (int i = 0; i < cityNum; i++)
  203. {
  204. for (int j = 0; j < cityNum; j++)
  205. {
  206. for (int k = 0; k < antNum; k++)
  207. {
  208. pheromone[i][j] += ants[k].getDelta()[i][j];
  209. }
  210. }
  211. }
  212. }
  213. private void printOptimal()
  214. {
  215. System.out.println("The optimal length is: " + bestLength);
  216. System.out.println("The optimal tour is: ");
  217. for (int i = 0; i < cityNum + 1; i++)
  218. {
  219. System.out.print(bestTour[i] + " ");
  220. }
  221. System.out.println();
  222. }
  223. public Ant[] getAnts()
  224. {
  225. return ants;
  226. }
  227. public void setAnts(Ant[] ants)
  228. {
  229. this.ants = ants;
  230. }
  231. public int getAntNum()
  232. {
  233. return antNum;
  234. }
  235. public void setAntNum(int m)
  236. {
  237. this.antNum = m;
  238. }
  239. public int getCityNum()
  240. {
  241. return cityNum;
  242. }
  243. public void setCityNum(int cityNum)
  244. {
  245. this.cityNum = cityNum;
  246. }
  247. public int getMAX_GEN()
  248. {
  249. return MAX_GEN;
  250. }
  251. public void setMAX_GEN(int mAX_GEN)
  252. {
  253. MAX_GEN = mAX_GEN;
  254. }
  255. public double[][] getPheromone()
  256. {
  257. return pheromone;
  258. }
  259. public void setPheromone(double[][] pheromone)
  260. {
  261. this.pheromone = pheromone;
  262. }
  263. public int[][] getDistance()
  264. {
  265. return distance;
  266. }
  267. public void setDistance(int[][] distance)
  268. {
  269. this.distance = distance;
  270. }
  271. public int getBestLength()
  272. {
  273. return bestLength;
  274. }
  275. public void setBestLength(int bestLength)
  276. {
  277. this.bestLength = bestLength;
  278. }
  279. public int[] getBestTour()
  280. {
  281. return bestTour;
  282. }
  283. public void setBestTour(int[] bestTour)
  284. {
  285. this.bestTour = bestTour;
  286. }
  287. public double getAlpha()
  288. {
  289. return alpha;
  290. }
  291. public void setAlpha(double alpha)
  292. {
  293. this.alpha = alpha;
  294. }
  295. public double getBeta()
  296. {
  297. return beta;
  298. }
  299. public void setBeta(double beta)
  300. {
  301. this.beta = beta;
  302. }
  303. public double getRho()
  304. {
  305. return rho;
  306. }
  307. public void setRho(double rho)
  308. {
  309. this.rho = rho;
  310. }
  311. }

  1. package tsp;
  2. import java.io.FileNotFoundException;
  3. import java.io.IOException;
  4. import java.util.logging.Level;
  5. import java.util.logging.Logger;
  6. /**
  7. * @author ychXu
  8. *
  9. */
  10. public class TSP
  11. {
  12. /**
  13. * @param args
  14. * @throws IOException
  15. */
  16. public static void main(String[] args) throws IOException
  17. {
  18. ACO aco = new ACO(48, 100, 1000, 1.0f, 5.0f, 0.5f);
  19. try
  20. {
  21. aco.init("D://1.tsp");
  22. aco.solve();
  23. }
  24. catch (FileNotFoundException ex)
  25. {
  26. Logger.getLogger(TSP.class.getName()).log(Level.SEVERE, null, ex);
  27. }
  28. catch (IOException ex)
  29. {
  30. Logger.getLogger(TSP.class.getName()).log(Level.SEVERE, null, ex);
  31. }
  32. }
  33. }


5. 总结

      蚁群算法和其它的启发式算法一样,在很多场合都得到了应用,并且取得了很好的结果。但是同样存在着很多的缺点,例如收敛速度慢,容易陷入局部最优,等等。对于这些问题,还需要进一步的研究和探索,另外蚁群算法的数学机理至今还没有得到科学的解释,这也是当前研究的热点和急需解决的问题之一。注:TSP数据文件以及两篇早期的关于蚁群算法的文章包含在附件中,请点击此处下载附件

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/663450
推荐阅读
相关标签
  

闽ICP备14008679号