|
@@ -11,18 +11,15 @@ public class AntColonyOptimizer
|
|
|
private double[][] distances;
|
|
|
private double[][] pheromones;
|
|
|
private readonly double evaporationRate;
|
|
|
- private readonly int numberOfAnts;
|
|
|
|
|
|
private readonly List<IAnt> xants = [];
|
|
|
- private IAnt[] ants;
|
|
|
+ private IAnt[]? ants;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Инициализировать алгоритм
|
|
|
/// </summary>
|
|
|
/// <param name="distances">Матрица расстояний</param>
|
|
|
- /// <param name="numberOfAnts">Количество муравьев</param>
|
|
|
/// <param name="evaporationRate">Скорость испарения</param>
|
|
|
- /// <param name="Q">Значение Q</param>
|
|
|
public AntColonyOptimizer(double[][] distances, double evaporationRate = 0.1)
|
|
|
{
|
|
|
numberOfCities = distances.GetLength(0);
|
|
@@ -43,20 +40,32 @@ public class AntColonyOptimizer
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public AntColonyOptimizer AddAnts<T>(int count) where T : IAnt
|
|
|
+ /// <summary>
|
|
|
+ /// Добавить определенное количество муравьев заданного типа
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="T">Тип муравьев</typeparam>
|
|
|
+ /// <param name="count">Количество муравьев</param>
|
|
|
+ /// <param name="args">Аргументы конструктора муравья</param>
|
|
|
+ public AntColonyOptimizer AddAnts<T>(int count, params object[] args) where T : IAnt
|
|
|
{
|
|
|
for (int i = 0; i < count; i++)
|
|
|
{
|
|
|
- xants.Add(Activator.CreateInstance<T>()!);
|
|
|
+ // Создаем нового муравья заданного типа
|
|
|
+ xants.Add((T)Activator.CreateInstance(typeof(T), args)!);
|
|
|
}
|
|
|
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public AntColonyOptimizer ShuffleAnts()
|
|
|
+ /// <summary>
|
|
|
+ /// Расположить всех муравьев в случайном порядке
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="shuffle">Расположить муравьев в случайном порядке</param>
|
|
|
+ public AntColonyOptimizer CompileAnts(bool shuffle = false)
|
|
|
{
|
|
|
ants = [.. xants];
|
|
|
- Random.Shared.Shuffle(ants);
|
|
|
+ // Если нужно, располагаем муравьев в случайном порядке
|
|
|
+ if (shuffle) Random.Shared.Shuffle(ants);
|
|
|
|
|
|
return this;
|
|
|
}
|
|
@@ -77,60 +86,58 @@ public class AntColonyOptimizer
|
|
|
/// <returns>Лучший путь и его расстояние</returns>
|
|
|
public (List<int> bestTour, double bestDistance) Solve(int maxIterations)
|
|
|
{
|
|
|
- // for (int iteration = 0; iteration < maxIterations; iteration++)
|
|
|
+ // Если колония муравьев пуста
|
|
|
+ if (ants is null) throw new MissingMemberException("Колония пуста. Забыли вызвать AntColonyOptimizer.CompileAnts()?");
|
|
|
+
|
|
|
+ Console.WriteLine($"Поиск кратчайшего Гамильтонова цикла с {ants.Length} муравьев и {maxIterations} итераций");
|
|
|
+
|
|
|
for (int i = 0; i < maxIterations; i++)
|
|
|
{
|
|
|
ConcurrentBag<List<int>> antTours = [];
|
|
|
ConcurrentBag<double> antDistances = [];
|
|
|
|
|
|
- Console.Write($"{i}/{maxIterations} - {bestDistanceEver}\r");
|
|
|
-
|
|
|
// Каждый муравей строит путь
|
|
|
var iterationTours = ants.AsParallel().Select(ant =>
|
|
|
{
|
|
|
var ct = ant.ConstructTour(ref distances, ref pheromones);
|
|
|
+ // Если муравей зашел в тупик - убиваем его
|
|
|
if (!ct.HasValue) return ([], double.MaxValue);
|
|
|
var (tour, distance) = ct.Value;
|
|
|
antTours.Add(tour);
|
|
|
antDistances.Add(distance);
|
|
|
- // Console.Write($"\r|- {i}/{numberOfAnts} - {distance}");
|
|
|
|
|
|
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;
|
|
|
-
|
|
|
- // Обновляем феромоны для всех путей
|
|
|
+ // Испаряем феромоны после итерации
|
|
|
EvaporatePheromones();
|
|
|
- // File.AppendAllText("./iteration-results", bestDistanceEver.ToString() + '\n');
|
|
|
- // }
|
|
|
|
|
|
+ // Находим лучший путь
|
|
|
(List<int> Tour, double Distance) bestIterationTour = iterationTours.MinBy(x => x.distance);
|
|
|
- if (bestIterationTour.Distance < bestDistanceEver)
|
|
|
+ if (bestIterationTour.Distance < bestDistanceEver) // Если лучше текущего - обновляем текущий
|
|
|
{
|
|
|
bestDistanceEver = bestIterationTour.Distance;
|
|
|
bestTourEver = bestIterationTour.Tour;
|
|
|
- Console.WriteLine();
|
|
|
+ Console.WriteLine($"{i} - {bestDistanceEver,20}");
|
|
|
}
|
|
|
- };
|
|
|
+
|
|
|
+ Console.Write($"{i}/{maxIterations} - {bestDistanceEver}\r");
|
|
|
+ }
|
|
|
|
|
|
return (bestTourEver, bestDistanceEver);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Обновить матрицу феромонов
|
|
|
+ /// Испарить феромоны
|
|
|
/// </summary>
|
|
|
private void EvaporatePheromones()
|
|
|
{
|
|
|
- // Испарение
|
|
|
for (int i = 0; i < numberOfCities; i++)
|
|
|
{
|
|
|
for (int j = 0; j < numberOfCities; j++)
|