public override void Run() { int n = 4096; Console.WriteLine($"Starting algorithm with {n} individuals, {_amountOfRounds} rounds, " + $"{_amountOfTeams} teams, and {_avgPlayersPerTeam} players per team."); Console.WriteLine("Initiating random population of schedules..."); // Construct n random solutions, which are permutations of one original random set List <BallStarsSchedule> population = InitRandomPopulation(n) .Select(indiv => indiv as BallStarsSchedule).ToList(); // Evolve over generations until a sufficiently good solution is found or time runs out. BallStarsSchedule bestSolution = population[0]; float bestFitness = bestSolution.Evaluate(); int currentGen = 0; while (bestFitness != 0f && currentGen < 1000) // TODO: Include timer if necessary { Console.WriteLine($"Commencing generation {currentGen}."); // Create offspring by applying crossover to the existing population var offspring = new List <BallStarsSchedule>(); if (_useLocalSearch) { // Local search for (int i = 0; i < population.Count; i++) { BallStarsSchedule clone = bestSolution.Clone(); offspring.Add(clone); } } else if (_useCrossover) { // Apply single-point crossover at a given cutoff point for (int i = 0; i < population.Count; i += 2) { (BallStarsSchedule o0, BallStarsSchedule o1) = population[i].Crossover(population[i + 1], _roundCrossoverCutoff); offspring.Add(o0); offspring.Add(o1); } } else { // Use cloning foreach (var individual in population) { BallStarsSchedule clone = individual.Clone(); offspring.Add(clone); } } // Mutate the offspring foreach (var schedule in offspring) { if (Globals.Rand.NextDouble() < 0.8) { schedule.AddSportsMatchFromPool(_matchPool); } // schedule.GranularMutate(); schedule.Mutate(); } // Evaluate both the population and the offspring population.ForEach(schedule => schedule.Evaluate()); offspring.ForEach(schedule => schedule.Evaluate()); // Select the best n individuals out of the population + offspring // population = NaiveSelection(population, offspring); population = TournamentSelection(population, offspring, 4); // Update bestFitness if possible foreach (var individual in population) { float fitness = individual.Fitness; if (fitness < bestFitness) { bestFitness = fitness; bestSolution = individual; Console.WriteLine($"New best fitness: {bestFitness} (found in generation {currentGen})"); } } currentGen++; } // Save the best solution to a file bestSolution.SaveToCsv(_outputFile); Console.WriteLine($"Algorithm finished. The best schedule is as follows:\n{bestSolution}"); Console.WriteLine($"The final schedule has been saved to {_outputFile}."); }
public void RunSimulatedAnnealing(double initialTemperature = 10, double alpha = 0.9999) { // Start off with a randomised schedule for an even amount of teams int eventsPerRound = _amountOfTeams / 2; BallStarsSchedule currentSchedule = BallStarsSchedule.Random( _amountOfTeams, eventsPerRound, eventsPerRound, _amountOfRounds, _matchPool, false, _avgPlayersPerTeam, _predefinedMatchUps ); BallStarsSchedule bestSchedule = currentSchedule; double bestFitness = currentSchedule.Evaluate(); double currentScheduleFitness = bestFitness; double minimumTemp = 0.000000001; double temperature = initialTemperature; int iters = 0; while (iters < 100000) { if (iters % 1000 == 0) { Console.WriteLine($"Running iteration {iters}."); } // Pick a neighbour by cloning and mutating BallStarsSchedule neighbour = currentSchedule.Clone(); neighbour.LocalSearchMutate(_matchPool); neighbour.Evaluate(); // Always accept better solutions if (neighbour.Fitness < currentScheduleFitness) { // Update currently tracking schedule and fitness currentScheduleFitness = neighbour.Fitness; currentSchedule = neighbour; // Update best found schedule and fitness if they improved if (currentScheduleFitness < bestFitness) { bestFitness = currentScheduleFitness; bestSchedule = currentSchedule.Clone(); Console.WriteLine($"New best fitness: {bestFitness} (found in iteration {iters})"); } } else { // Accept worse solutions often when starting out, but not as much near termination double diff = neighbour.Fitness - currentSchedule.Fitness; double acceptanceProb = 1 / (1 + Math.Exp(diff / temperature)); if (Globals.Rand.NextDouble() < acceptanceProb) { currentSchedule = neighbour; currentScheduleFitness = neighbour.Fitness; } } temperature *= alpha; iters++; // TODO: Maybe add a temperature reset to get out of a local minimum } Console.WriteLine($"Algorithm finished. The best schedule is as follows:\n{bestSchedule}"); }