|
@@ -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;
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
|
|
|
|
|
|
- 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;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -61,7 +71,7 @@ public class AntColonyOptimizer
|
|
|
private double bestDistanceEver = double.PositiveInfinity;
|
|
|
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -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();
|
|
|
-
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- UpdatePheromones([.. antTours], [.. antDistances]);
|
|
|
+ EvaporatePheromones();
|
|
|
|
|
|
|
|
|
|
|
@@ -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));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
-
|
|
|
- for (int i = 0; i < numberOfCities; i++)
|
|
|
- {
|
|
|
- if (!visited[i])
|
|
|
- {
|
|
|
-
|
|
|
- if (probabilities[i] >= randomValue)
|
|
|
- {
|
|
|
- return i;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
}
|
|
|
}
|