public IEnumerable <int> GetMatchedTiles() { // Cut tiles from chromosome var cutTiles = E2Chromosome.CutTiles(this); // Create a copy of the real tiles for us to work with var comp = Tiles.ToList(); var tileIndex = 0; foreach (var tile in cutTiles) { // Walk our own list of all the real tiles... for (var n = 0; n < comp.Count; n++) { if (comp[n].Matches(tile)) { yield return(tileIndex); comp.RemoveAt(n); break; } } tileIndex++; } }
public static IEnumerable <string> CutTiles(E2Chromosome chromosome) { // generate set of strings representing the tiles cut from the candidate solution for (var y = 0; y < E2Chromosome.Height; y++) { for (var x = 0; x < E2Chromosome.Width; x++) { int root = y * (E2Chromosome.Width * 2 + 1) + x; yield return ($"{(char) chromosome.GetGene(root + E2Chromosome.Width).Value}{(char) chromosome.GetGene(root).Value}{(char) chromosome.GetGene(root + E2Chromosome.Width + 1).Value}{(char) chromosome.GetGene(root + (E2Chromosome.Width * 2 + 1)).Value}"); } } }
public (int, double) GetMatchedTileScore() { // Cut tiles from chromosome var cutTiles = E2Chromosome.CutTiles(this).ToList(); // Create a copy of the real tiles for us to work with var comp = Tiles.ToList(); var score = 0.0; var tileMatches = 0; foreach (var tile in cutTiles) { // Walk our own list of all the real tiles... for (var n = 0; n < comp.Count; n++) { if (comp[n].Matches(tile)) { score++; tileMatches++; comp.RemoveAt(n); break; } } } // Walk the list again and look for partial matches new int[] { 4, 3 }.Each(threshold => { foreach (var tile in cutTiles) { for (var n = 0; n < comp.Count; n++) { if (comp[n].SymbolMatches(tile) == threshold) { score += ((double)threshold / 24.0); comp.RemoveAt(n); break; } } } }); return(tileMatches, score); }
public double Evaluate(IChromosome chromosome) { // Cast our chromosome to our own type E2Chromosome c = (E2Chromosome)chromosome; // Cut the chromosome into tiles, then compare those to the known set... How many match? var matchScore = c.GetMatchedTileScore(); c.Matches = matchScore.Item1; if (c.Matches == 0) { return(0); // No matches is a zero fit :( } // Fitness (as a 0..1 number) is the completeness of the solution double fitness = (double)matchScore.Item2 / (double)E2Chromosome.TileCount; // How valid is this solution? // We could have a great number of matches, but we might have built this from an invalid symbol // set, i.e. too many As, not enough Bs // Sort the symbols used in this chromosome and compare item by item with the know sorted set // validity is the count of matches string solSymbols = c.ToSortedSymbolList(); int validity = Enumerable.Range(0, solSymbols.Length - 1) .Count(n => solSymbols[n] == E2Chromosome.SymbolsList[n]); // If we have some invalidity then divide fitness significantly var diff = E2Chromosome.TileCount - validity; if (diff > 0) { fitness /= diff; } // Record fitness c.FitnessValue = fitness; return(fitness); }
static void Main(string[] args) { // var chromosome = new E2Chromosome(5, 5); // Console.WriteLine(chromosome.ToString()); // // int n = 0; // foreach (var cutTile in E2Chromosome.CutTiles(chromosome)) // { // Console.Write(cutTile + " "); // if ( ++n % 5 == 0)Console.WriteLine(); // } // // Console.ReadLine(); // Setup problem E2Chromosome.Width = 5; E2Chromosome.Height = 5; E2Chromosome.TileCount = E2Chromosome.Width * E2Chromosome.Height; E2Chromosome.Tiles = new string[] { "ABDC", "DEBF", "BGGE", "GCFF", "FEED", "CCBF", "BFCC", "CEGG", "GFAH", "ADDC", "FFAC", "ACHE", "HGBC", "BHAF", "ACHG", "ECFC", "FEDB", "DCGB", "GFCA", "CGFD", "ECBE", "BBGC", "GBCH", "CAHD", "HDCE" }.Select(t => new Tile(t)); // Create chromosome and fitness var chromosome = new E2Chromosome(); var fitness = new E2Fitness(); // This operators are classic genetic algorithm operators that lead to a good solution on TSP, // but you can try others combinations and see what result you get. var crossover = new GeneticSharp.Domain.Crossovers.OnePointCrossover(); var mutation = new GeneticSharp.Domain.Mutations.PartialShuffleMutation(); var selection = new GeneticSharp.Domain.Selections.RouletteWheelSelection(); var population = new GeneticSharp.Domain.Populations.Population(5000, 10000, chromosome); ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation); ga.MutationProbability = 0.1f; ga.Termination = new GeneticSharp.Domain.Terminations.FitnessThresholdTermination(1.0); // The fitness evaluation of whole population will be running on parallel. ga.TaskExecutor = new ParallelTaskExecutor { MinThreads = 100, MaxThreads = 200 }; // Every time a generation ends, we log the best solution. int bestMatches = 0; DateTime startTime = DateTime.Now; ga.GenerationRan += delegate { var best = ((E2Chromosome)ga.BestChromosome); var Matches = best.Matches; var Fitness = best.FitnessValue; if (Matches > bestMatches || ga.GenerationsNumber % 250 == 0) { Console.WriteLine( $"{DateTime.Now.Subtract(startTime).TotalSeconds}s Generation: {ga.GenerationsNumber} - Matches: {Matches} - Fitness: {Fitness}"); if (Matches > bestMatches) { Console.WriteLine(((E2Chromosome)ga.BestChromosome).ToStringIncludingTileMatches(true)); } bestMatches = Matches; } }; // Starts the genetic algorithm in a separate thread. gaThread = new Thread(() => ga.Start()); gaThread.Start(); Console.ReadLine(); // When the script is destroyed we stop the genetic algorithm and abort its thread too. ga.Stop(); gaThread.Abort(); }