| 
					
				 | 
			
			
				@@ -1,5 +1,4 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using System.Collections.Concurrent; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-using System.Reflection.Metadata.Ecma335; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 namespace AntColony.Algorithm; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -9,33 +8,26 @@ namespace AntColony.Algorithm; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private readonly int numberOfCities; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private readonly double[][] distances; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private readonly double[][] pheromones; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private readonly double alpha; // Важность феромонов 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private readonly double beta;  // Важность расстояния  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private double[][] distances; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private double[][] pheromones; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private readonly double evaporationRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private readonly double Q;     // Множитель депозита феромонов 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private readonly int numberOfAnts; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private readonly List<IAnt> xants = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private IAnt[] ants; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Инициализировать алгоритм 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <param name="distances">Матрица расстояний</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <param name="numberOfAnts">Количество муравьев</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="alpha">Значение α (определяет вес феромонов)</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="beta">Значение β (определяет вес расстояния)</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <param name="evaporationRate">Скорость испарения</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <param name="Q">Значение Q</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public AntColonyOptimizer(double[][] distances, int numberOfAnts = 20, double alpha = 1, double beta = 2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        double evaporationRate = 0.1, double Q = 100) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public AntColonyOptimizer(double[][] distances, double evaporationRate = 0.1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         numberOfCities = distances.GetLength(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         this.distances = distances; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        this.numberOfAnts = numberOfAnts; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        this.alpha = alpha; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        this.beta = beta; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         this.evaporationRate = evaporationRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        this.Q = Q; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // Строим стартовую матрицу феромонов 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         pheromones = new double[numberOfCities][]; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -51,6 +43,24 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public AntColonyOptimizer AddAnts<T>(int count) where T : IAnt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (int i = 0; i < count; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            xants.Add(Activator.CreateInstance<T>()!); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return this; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public AntColonyOptimizer ShuffleAnts() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ants = [.. xants]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Random.Shared.Shuffle(ants); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return this; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Обработчик ранней остановки работы алгоритма 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// </summary> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -61,7 +71,7 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private double bestDistanceEver = double.PositiveInfinity; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Решить задачу комивояжера (построить Гамильтонов граф) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Решить задачу комивояжера (найти Гамильтонов цикл) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <param name="maxIterations">Максимальное число итераций</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <returns>Лучший путь и его расстояние</returns> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -76,10 +86,9 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Console.Write($"{i}/{maxIterations} - {bestDistanceEver}\r"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // Каждый муравей строит путь 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var iterationTours = Enumerable.Range(0, numberOfAnts).AsParallel().Select(i => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var iterationTours = ants.AsParallel().Select(ant => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var ct = ConstructTour(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // while (!ct.HasValue) ct = ConstructTour(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var ct = ant.ConstructTour(ref distances, ref pheromones); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!ct.HasValue) return ([], double.MaxValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var (tour, distance) = ct.Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 antTours.Add(tour); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -89,13 +98,18 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return (tour, distance); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (int j = 0; j < antTours.Count; j++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ants[j].UpdatePheromones(ref pheromones); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // Console.Write(new string(' ', 20)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // Console.CursorTop--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // Console.CursorLeft = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // Обновляем феромоны для всех путей 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            UpdatePheromones([.. antTours], [.. antDistances]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            EvaporatePheromones(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // File.AppendAllText("./iteration-results", bestDistanceEver.ToString() + '\n'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -111,81 +125,10 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return (bestTourEver, bestDistanceEver); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private (List<int> Tour, double Distance)? ConstructTour() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        List<int> tour = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var visited = new bool[numberOfCities]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var currentCity = Random.Shared.Next(numberOfCities); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        tour.Add(currentCity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        visited[currentCity] = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Строим путь 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        while (tour.Count < numberOfCities) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var sel = SelectNextCity(currentCity, visited); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            while (!sel.HasValue) sel = SelectNextCity(currentCity, visited); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (sel is not { } nextCity) return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tour.Add(nextCity); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            visited[nextCity] = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            currentCity = nextCity; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (distances[currentCity][tour[0]] == 0) return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Добавляем возврат на начальную точку 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        tour.Add(tour[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return (tour, CalculateTourDistance(tour)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Выбрать следующий город 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="currentCity">В каком городе муравей находится сейчас</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="visited">Какие города муравей уже посетил</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <returns>Номер следующего города</returns> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private int? SelectNextCity(int currentCity, bool[] visited) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var probabilities = new double[numberOfCities]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        double probabilitiesSum = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Вероятности для каждого непосещенного города 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (int i = 0; i < numberOfCities; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!visited[i]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (distances[currentCity][i] == 0) continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                probabilities[i] = Math.Pow(pheromones[currentCity][i], alpha) * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                 Math.Pow(1.0 / distances[currentCity][i], beta); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                probabilitiesSum += probabilities[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Случайно выбираем следующий город (по вероятностям) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        double randomValue = Random.Shared.NextDouble() * probabilitiesSum; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //double sum = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (int i = 0; i < numberOfCities; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!visited[i]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                //sum += probabilities[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (probabilities[i] >= randomValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Обновить матрицу феромонов 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="antTours">Пути муравьев</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="antDistances">Их расстояния</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private void UpdatePheromones(List<List<int>> antTours, List<double> antDistances) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void EvaporatePheromones() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // Испарение 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for (int i = 0; i < numberOfCities; i++) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -196,35 +139,5 @@ public class AntColonyOptimizer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     pheromones[i][j] *= 1.0 - evaporationRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Добавляем новые феромоны 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (int ant = 0; ant < antTours.Count; ant++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            double pheromoneDeposit = Q / antDistances[ant]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var tour = antTours[ant]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            for (int i = 0; i < tour.Count - 1; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                int city1 = tour[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                int city2 = tour[i + 1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                pheromones[city1][city2] += pheromoneDeposit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                pheromones[city2][city1] += pheromoneDeposit; // Граф ненаправленный 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Рассчитать длину пути 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <param name="tour">Путь</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// <returns>Длина пути</returns> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private double CalculateTourDistance(List<int> tour) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        double distance = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (int i = 0; i < tour.Count - 1; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            distance += distances[tour[i]][tour[i + 1]]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return distance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |