Example #1
0
        /// <summary>
        /// Selects an amount of individuals equal to the given population's size by applying tournament selection,
        /// where the fittest individuals are taken from n random samples of a given tournament size.
        /// </summary>
        /// <param name="population"></param>
        /// <param name="offspring"></param>
        /// <param name="tournamentSize">The amount of individuals in each tournament. Larger tournament size results in
        /// a lower probability of selecting weaker individuals</param>
        /// <returns></returns>
        private List <BallStarsSchedule> TournamentSelection(List <BallStarsSchedule> population,
                                                             List <BallStarsSchedule> offspring, int tournamentSize)
        {
            int n = population.Count;

            // Combine the population and offspring
            population.AddRange(offspring);

            BallStarsSchedule[] selected = new BallStarsSchedule[n];
            for (int i = 0; i < n; i++)
            {
                // Track the tournament winner and its fitness
                double            bestFitness      = Int32.MaxValue;
                BallStarsSchedule tournamentWinner = null;
                for (int j = 0; j < tournamentSize; j++)
                {
                    BallStarsSchedule randomSchedule = population[Globals.Rand.Next(population.Count)];
                    if (randomSchedule.Fitness < bestFitness)
                    {
                        bestFitness      = randomSchedule.Fitness;
                        tournamentWinner = randomSchedule;
                    }
                }

                selected[i] = tournamentWinner;
            }

            return(selected.ToList());
        }
Example #2
0
        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);
        }
Example #3
0
        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}.");
        }
Example #4
0
        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}");
        }