namespace AntColony.Algorithm;
///
/// Стандартная реализация муравья
///
/// Значение α (определяет вес феромонов)
/// Значение β (определяет вес расстояния)
/// Значение Q (множитель депозита феромонов)
public class DefaultAnt(double alpha = 1, double beta = 1, double Q = 100) : IAnt
{
private List tour = [];
private double distance = double.PositiveInfinity;
///
public (List tour, double distance)? ConstructTour(ref double[][] distances, ref double[][] pheromones)
{
tour = [];
distance = double.PositiveInfinity;
var visited = new bool[distances.Length];
var curCity = Random.Shared.Next(distances.Length);
tour.Add(curCity);
visited[curCity] = true;
// Пока остались непосещенные города
while (tour.Count < distances.Length)
{
var sel = SelectNextCity(curCity, ref visited, ref distances, ref pheromones);
while (!sel.HasValue) sel = SelectNextCity(curCity, ref visited, ref distances, ref pheromones);
// Если не можем найти следующий город - самоубиваемся
if (sel is not { } nextCity) return null;
tour.Add(nextCity);
visited[nextCity] = true;
curCity = nextCity;
}
// Если не можем найти путь до стартовой точки - самоубиваемся
if (distances[curCity][tour[0]] == 0) return null;
tour.Add(tour[0]);
distance = CalculateTourDistance(tour, ref distances);
return (tour, distance);
}
///
public void UpdatePheromones(ref double[][] pheromones)
{
double deposit = Q / distance;
for (int i = 0; i < tour.Count - 1; i++)
{
int cityA = tour[i];
int cityB = tour[i + 1];
pheromones[cityA][cityB] += deposit;
pheromones[cityB][cityA] += deposit; // Ненаправленный граф
}
}
private static double CalculateTourDistance(List tour, ref double[][] distances)
{
double distance = 0;
for (int i = 0; i < tour.Count - 1; i++)
{
distance += distances[tour[i]][tour[i + 1]];
}
return distance;
}
private int? SelectNextCity(int currentCity, ref bool[] visited, ref double[][] distances, ref double[][] pheromones)
{
var probabilities = new double[distances.Length];
double probabilitiesSum = 0;
// Вероятности для каждого непосещенного города
for (int i = 0; i < distances.Length; 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 < distances.Length; i++)
{
if (!visited[i])
{
//sum += probabilities[i];
if (probabilities[i] >= randomValue)
{
return i;
}
}
}
return null;
}
}