1
0

ACO.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. namespace AntColony.Algorithm;
  2. /// <summary>
  3. /// Алгоритм построения кратчайшего пути с помощью симуляции муравьиной колонии
  4. /// </summary>
  5. public class AntColonyOptimizer
  6. {
  7. private readonly int numberOfCities;
  8. private readonly double[][] distances;
  9. private readonly double[][] pheromones;
  10. private readonly double alpha; // Важность феромонов
  11. private readonly double beta; // Важность расстояния
  12. private readonly double evaporationRate;
  13. private readonly double Q; // Множитель депозита феромонов
  14. private readonly int numberOfAnts;
  15. /// <summary>
  16. /// Инициализировать алгоритм
  17. /// </summary>
  18. /// <param name="distances">Матрица расстояний</param>
  19. /// <param name="numberOfAnts">Количество муравьев</param>
  20. /// <param name="alpha">Значение α (определяет вес феромонов)</param>
  21. /// <param name="beta">Значение β (определяет вес расстояния)</param>
  22. /// <param name="evaporationRate">Скорость испарения</param>
  23. /// <param name="Q">Значение Q</param>
  24. public AntColonyOptimizer(double[][] distances, int numberOfAnts = 20, double alpha = 1, double beta = 2,
  25. double evaporationRate = 0.1, double Q = 100)
  26. {
  27. numberOfCities = distances.GetLength(0);
  28. this.distances = distances;
  29. this.numberOfAnts = numberOfAnts;
  30. this.alpha = alpha;
  31. this.beta = beta;
  32. this.evaporationRate = evaporationRate;
  33. this.Q = Q;
  34. // Строим стартовую матрицу феромонов
  35. pheromones = new double[numberOfCities][];
  36. for (int i = 0; i < numberOfCities; i++)
  37. {
  38. var r = new double[numberOfCities];
  39. for (int j = 0; j < numberOfCities; j++)
  40. {
  41. if (i != j)
  42. r[j] = 1.0; // Начальное значение
  43. }
  44. pheromones[i] = r;
  45. }
  46. }
  47. /// <summary>
  48. /// Решить задачу комивояжера (построить Гамильтонов граф)
  49. /// </summary>
  50. /// <param name="maxIterations">Максимальное число итераций</param>
  51. /// <returns>Лучший путь и его расстояние</returns>
  52. public (List<int> bestTour, double bestDistance) Solve(int maxIterations)
  53. {
  54. List<int> bestTourEver = [];
  55. double bestDistanceEver = double.MaxValue;
  56. Lock distanceLock = new();
  57. Lock pheromoneLock = new();
  58. // for (int iteration = 0; iteration < maxIterations; iteration++)
  59. Parallel.For(0, maxIterations, i =>
  60. {
  61. List<List<int>> antTours = [];
  62. List<double> antDistances = [];
  63. // Каждый муравей строит путь
  64. for (int ant = 0; ant < numberOfAnts; ant++)
  65. {
  66. var ct = ConstructTour();
  67. if (ct is null) continue;
  68. var (tour, distance) = ct.Value;
  69. antTours.Add(tour);
  70. antDistances.Add(distance);
  71. // Если текущий путь лучше - обновляем
  72. lock (distanceLock)
  73. {
  74. if (distance < bestDistanceEver)
  75. {
  76. bestDistanceEver = distance;
  77. bestTourEver = [.. tour];
  78. }
  79. }
  80. }
  81. // Обновляем феромоны для всех путей
  82. lock (pheromoneLock)
  83. {
  84. UpdatePheromones(antTours, antDistances);
  85. }
  86. // File.AppendAllText("./iteration-results", bestDistanceEver.ToString() + '\n');
  87. // }
  88. });
  89. return (bestTourEver, bestDistanceEver);
  90. }
  91. private (List<int> tour, double distance)? ConstructTour()
  92. {
  93. List<int> tour = [];
  94. var visited = new bool[numberOfCities];
  95. var currentCity = Random.Shared.Next(numberOfCities);
  96. tour.Add(currentCity);
  97. visited[currentCity] = true;
  98. // Строим путь
  99. while (tour.Count < numberOfCities)
  100. {
  101. var sel = SelectNextCity(currentCity, visited);
  102. if (sel is not { } nextCity) return null;
  103. tour.Add(nextCity);
  104. visited[nextCity] = true;
  105. currentCity = nextCity;
  106. }
  107. if (distances[currentCity][tour[0]] == 0) return null;
  108. // Добавляем возврат на начальную точку
  109. tour.Add(tour[0]);
  110. return (tour, CalculateTourDistance(tour));
  111. }
  112. /// <summary>
  113. /// Выбрать следующий город
  114. /// </summary>
  115. /// <param name="currentCity">В каком городе муравей находится сейчас</param>
  116. /// <param name="visited">Какие города муравей уже посетил</param>
  117. /// <returns>Номер следующего города</returns>
  118. private int? SelectNextCity(int currentCity, bool[] visited)
  119. {
  120. var probabilities = new double[numberOfCities];
  121. double probabilitiesSum = 0;
  122. // Вероятности для каждого непосещенного города
  123. for (int i = 0; i < numberOfCities; i++)
  124. {
  125. if (!visited[i])
  126. {
  127. if (distances[currentCity][i] == 0) continue;
  128. probabilities[i] = Math.Pow(pheromones[currentCity][i], alpha) *
  129. Math.Pow(1.0 / distances[currentCity][i], beta);
  130. probabilitiesSum += probabilities[i];
  131. }
  132. }
  133. // Случайно выбираем следующий город (по вероятностям)
  134. double randomValue = Random.Shared.NextDouble() * probabilitiesSum;
  135. //double sum = 0;
  136. for (int i = 0; i < numberOfCities; i++)
  137. {
  138. if (!visited[i])
  139. {
  140. //sum += probabilities[i];
  141. if (probabilities[i] >= randomValue)
  142. return i;
  143. }
  144. }
  145. return null;
  146. }
  147. /// <summary>
  148. /// Обновить матрицу феромонов
  149. /// </summary>
  150. /// <param name="antTours">Пути муравьев</param>
  151. /// <param name="antDistances">Их расстояния</param>
  152. private void UpdatePheromones(List<List<int>> antTours, List<double> antDistances)
  153. {
  154. // Испарение
  155. for (int i = 0; i < numberOfCities; i++)
  156. {
  157. for (int j = 0; j < numberOfCities; j++)
  158. {
  159. if (i != j)
  160. pheromones[i][j] *= 1.0 - evaporationRate;
  161. }
  162. }
  163. // Добавляем новые феромоны
  164. for (int ant = 0; ant < antTours.Count; ant++)
  165. {
  166. double pheromoneDeposit = Q / antDistances[ant];
  167. var tour = antTours[ant];
  168. for (int i = 0; i < tour.Count - 1; i++)
  169. {
  170. int city1 = tour[i];
  171. int city2 = tour[i + 1];
  172. pheromones[city1][city2] += pheromoneDeposit;
  173. pheromones[city2][city1] += pheromoneDeposit; // Граф ненаправленный
  174. }
  175. }
  176. }
  177. /// <summary>
  178. /// Рассчитать длину пути
  179. /// </summary>
  180. /// <param name="tour">Путь</param>
  181. /// <returns>Длина пути</returns>
  182. private double CalculateTourDistance(List<int> tour)
  183. {
  184. double distance = 0;
  185. for (int i = 0; i < tour.Count - 1; i++)
  186. {
  187. distance += distances[tour[i]][tour[i + 1]];
  188. }
  189. return distance;
  190. }
  191. }