protected override List <Individual.Individual> InitRandomPopulation(int amountOfIndividuals) { var population = new List <Individual.Individual>(); // If there is an odd amount of teams, add a break event to every round. bool addBreakRound = _amountOfTeams % 2 == 1; int eventsPerRound = addBreakRound ? _amountOfTeams / 2 + 1 : _amountOfTeams / 2; int regularEventsPerRound = addBreakRound ? eventsPerRound - 1 : eventsPerRound; Console.WriteLine($"-> Creating {amountOfIndividuals} schedules with {eventsPerRound} events per round."); for (int i = 0; i < amountOfIndividuals; i++) { population.Add(BallStarsSchedule.Random( _amountOfTeams, eventsPerRound, regularEventsPerRound, _amountOfRounds, _matchPool, addBreakRound, _avgPlayersPerTeam, _predefinedMatchUps )); } return(population); }
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}"); }