Created
December 13, 2013 16:09
-
-
Save deejaygraham/7946566 to your computer and use it in GitHub Desktop.
Reworking of rosetta code implementation to make slightly more generic. http://rosettacode.org/wiki/Evolutionary_algorithm#C.23
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Linq; | |
using System.Text; | |
namespace WeaselConsole | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
GeneticAlgorithm<string> algo = new GeneticAlgorithm<string> | |
{ | |
PopulationSize = 100, | |
MutationRate = 0.05, | |
GenerationLimit = 100 | |
}; | |
string target = "METHINKS IT IS LIKE A WEASEL"; | |
algo.Mutator = new WeaselMutator(); | |
var scorer = new WeaselFitnessScore(target); | |
algo.FitnessScore = scorer; | |
algo.TargetMatch = scorer; | |
algo.Population = new WeaselPopulationFactory(target.Length); | |
algo.Start += (obj, e) => | |
{ | |
Console.WriteLine("START: {0,20} fitness: {1}", e.InitialCondition, e.Score); | |
}; | |
algo.Progress += (obj, e) => | |
{ | |
Console.WriteLine(" #{0,6} {1,20} fitness: {2}", e.Generation, e.Best, e.Score); | |
}; | |
algo.Finish += (obj, e) => | |
{ | |
Console.WriteLine("END: #{0,6} {1,20}", e.Generation, e.FinalCondition); | |
}; | |
//const string target = "METHINKS IT IS LIKE A WEASEL"; | |
algo.Run(); | |
Console.ReadKey(); | |
} | |
} | |
// | |
public interface ICrossover<T> | |
{ | |
T Mutate(T instance, double rate); | |
} | |
public interface IMutator<T> | |
{ | |
T Mutate(T instance, double rate); | |
} | |
public class WeaselMutator : IMutator<string> | |
{ | |
private Random _rng; | |
public WeaselMutator() | |
{ | |
this._rng = new Random((int)DateTime.Now.Ticks); | |
} | |
public string Mutate(string current, double rate) | |
{ | |
return String.Join("", from c in current | |
select _rng.NextDouble() <= rate ? _rng.NextCharacter() : c); | |
} | |
} | |
public interface IPopulationFactory<T> | |
{ | |
T Create(); | |
Collection<T> Create(int populationSize); | |
Collection<T> CreateFrom(Collection<T> generation); | |
} | |
public class WeaselPopulationFactory : IPopulationFactory<string> | |
{ | |
private Random _rng; | |
public WeaselPopulationFactory(int length) | |
{ | |
this.Length = length; | |
this._rng = new Random((int)DateTime.Now.Ticks); | |
} | |
private int Length { get; set; } | |
public string Create() | |
{ | |
return this._rng.NextString(this.Length); | |
} | |
public Collection<string> Create(int populationSize) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Collection<string> CreateFrom(Collection<string> generation) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
public interface IFitnessScore<T> | |
{ | |
int Score(T instance); | |
} | |
public interface IMatchesTarget<T> | |
{ | |
bool MatchesTarget(T instance); | |
} | |
public class WeaselFitnessScore : IFitnessScore<string>, IMatchesTarget<string> | |
{ | |
public WeaselFitnessScore(string target) | |
{ | |
this.Target = target; | |
} | |
public string Target { get; set; } | |
public int Score(string current) | |
{ | |
return this.Target.Zip(current, (a, b) => a == b ? 1 : 0).Sum(); | |
} | |
public bool MatchesTarget(string instance) | |
{ | |
return this.Target == instance; | |
} | |
} | |
public class InitialConditionEventArgs<T> : EventArgs | |
{ | |
public InitialConditionEventArgs(T initial, int score) | |
{ | |
this.InitialCondition = initial; | |
this.Score = score; | |
} | |
public T InitialCondition { get; set; } | |
public int Score { get; set; } | |
} | |
public class FitnessProgressEventArgs<T> : EventArgs | |
{ | |
public FitnessProgressEventArgs(int attempt, T best, int score) | |
{ | |
this.Generation = attempt; | |
this.Best = best; | |
this.Score = score; | |
} | |
public int Generation { get; set; } | |
public T Best { get; set; } | |
public int Score { get; set; } | |
} | |
public class FinalConditionEventArgs<T> : EventArgs | |
{ | |
public FinalConditionEventArgs(int attempt, T final, int score) | |
{ | |
this.Generation = attempt; | |
this.FinalCondition = final; | |
this.Score = score; | |
} | |
public int Generation { get; set; } | |
public T FinalCondition { get; set; } | |
public int Score { get; set; } | |
} | |
public class GeneticAlgorithm<T> | |
{ | |
public event EventHandler<InitialConditionEventArgs<T>> Start; | |
public event EventHandler<FitnessProgressEventArgs<T>> Progress; | |
public event EventHandler<FinalConditionEventArgs<T>> Finish; | |
public IPopulationFactory<T> Population { get; set; } | |
public IMatchesTarget<T> TargetMatch { get; set; } | |
public IMutator<T> Mutator { get; set; } | |
public IFitnessScore<T> FitnessScore { get; set; } | |
public GeneticAlgorithm() | |
{ | |
this.PopulationSize = 100; | |
this.MutationRate = 0.05; | |
this.GenerationLimit = 100; | |
} | |
public int PopulationSize { get; set; } | |
public double MutationRate { get; set; } | |
public int GenerationLimit { get; set; } | |
public void Run() | |
{ | |
T parent = this.Population.Create(); | |
if (this.Start != null) | |
this.Start(this, new InitialConditionEventArgs<T>(parent, this.FitnessScore.Score(parent))); | |
int attempt = 0; | |
while (!this.TargetMatch.MatchesTarget(parent) && attempt < GenerationLimit) | |
{ | |
// Create C mutated strings + the current parent. | |
var candidates = (from child in Enumerable.Repeat(parent, PopulationSize) | |
select this.Mutator.Mutate(child, MutationRate)) | |
.Concat(Enumerable.Repeat(parent, 1)); | |
// Sort the strings by the fitness function. | |
var sorted = from candidate in candidates | |
orderby this.FitnessScore.Score(candidate) descending | |
select candidate; | |
// New parent is the most fit candidate. | |
parent = sorted.First(); | |
++attempt; | |
if (this.Progress != null) | |
this.Progress(this, new FitnessProgressEventArgs<T>(attempt, parent, this.FitnessScore.Score(parent))); | |
} | |
if (this.Finish != null) | |
this.Finish(this, new FinalConditionEventArgs<T>(attempt, parent, this.FitnessScore.Score(parent))); | |
} | |
} | |
public static class RandomExtensions | |
{ | |
public static char NextCharacter(this Random self) | |
{ | |
const string AllowedChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
return AllowedChars[self.Next() % AllowedChars.Length]; | |
} | |
public static string NextString(this Random self, int length) | |
{ | |
return String.Join("", Enumerable.Repeat(' ', length) | |
.Select(c => self.NextCharacter())); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment