Skip to content

Instantly share code, notes, and snippets.

@shaunhey
Created January 31, 2016 21:14
Show Gist options
  • Save shaunhey/991bcb1246565967feeb to your computer and use it in GitHub Desktop.
Save shaunhey/991bcb1246565967feeb to your computer and use it in GitHub Desktop.
Playing with simple genetic algorithms...
using System;
using System.Collections.Generic;
using System.Text;
namespace ml
{
public class Citizen
{
private static int _nextId = 0;
public int Id { get; private set; }
public int Fitness { get; private set; }
public string Value { get; set; }
// Not necessary, but fun...
public Citizen Mother { get; set; }
public Citizen Father { get; set; }
public Citizen()
{
Id = _nextId;
_nextId++;
Fitness = int.MaxValue;
Value = "";
Mother = null;
Father = null;
}
public override string ToString()
{
return string.Format("Id: {0:D6}, Value: {1}, Fitness: {2:D4}, Mother: {3:D6}, Father: {4:D6}",
Id, Value, Fitness, (Mother == null) ? 0 : Mother.Id, (Father == null) ? 0 : Father.Id);
}
public void SetFitnessToTarget(string target)
{
int fitness = 0;
for (int i = 0; i < target.Length; i++)
{
int difference = target[i] - Value[i];
fitness += Math.Abs(difference);
}
Fitness = fitness;
}
}
public class Simulation
{
private readonly string POSSIBLE_CHARACTERS = " `1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?";
private List<Citizen> m_population;
private Random m_random = new Random();
private string m_target = "";
private int m_populationSize = 0;
private double m_mutationRate = 0.0;
DateTime m_startTime;
DateTime m_endTime;
public Simulation(string target, int populationSize, double mutationRate)
{
m_target = target;
m_populationSize = populationSize;
m_mutationRate = mutationRate;
m_population = new List<Citizen>(populationSize);
Console.WriteLine("Target: {0}, Population Size: {1}, Mutation Rate: {2}", target, populationSize, mutationRate);
// The first generation. About the same intelligence as most politicians...
for (int i = 0; i < populationSize; i++)
{
Citizen c = new Citizen();
c.Value = GenerateRandomString(target.Length);
m_population.Add(c);
}
}
void CalculateTheFitnessOfTheCitizens()
{
m_population.ForEach((c) => { c.SetFitnessToTarget(m_target); });
}
void SortThePopulationByFitness()
{
m_population.Sort((left, right) => left.Fitness.CompareTo(right.Fitness));
}
void RemoveTheWeakestCitizens()
{
m_population.RemoveRange(m_populationSize / 2, m_populationSize / 2);
}
string GenerateRandomString(int length)
{
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
sb.Append(POSSIBLE_CHARACTERS[m_random.Next(POSSIBLE_CHARACTERS.Length)]);
}
return sb.ToString();
}
void ApplyRandomMutation(ref StringBuilder value)
{
value[m_random.Next(value.Length)] = POSSIBLE_CHARACTERS[m_random.Next(POSSIBLE_CHARACTERS.Length)];
}
void GetRandomParents(out Citizen mother, out Citizen father)
{
int a = m_random.Next(m_population.Count);
int b = m_random.Next(m_population.Count);
while (b == a) //Don't allow the same parent for both
{
b = m_random.Next(m_population.Count);
}
mother = m_population[a];
father = m_population[b];
}
Citizen Mate(Citizen mother, Citizen father)
{
StringBuilder newValue = new StringBuilder();
Citizen child = new Citizen();
child.Mother = mother;
child.Father = father;
for (int i = 0; i < mother.Value.Length; i++)
{
// Determine dominant features randomly
bool fatherIsDominant = m_random.NextDouble() < .5;
if (fatherIsDominant)
newValue.Append(father.Value[i]);
else
newValue.Append(mother.Value[i]);
}
double chanceOfMutation = m_random.NextDouble();
if (chanceOfMutation < m_mutationRate)
ApplyRandomMutation(ref newValue);
child.Value = newValue.ToString();
return child;
}
void MateThePopulation()
{
var parents = m_population;
var children = new List<Citizen>(m_populationSize);
for (int i = 0; i < m_populationSize; i++)
{
Citizen mother = null;
Citizen father = null;
GetRandomParents(out mother, out father);
var child = Mate(mother, father);
children.Add(child);
}
m_population = children; //Children are the future...
}
void PrintPaternalLineage(Citizen c)
{
Citizen father = c.Father;
Console.Write("{0} ", c.Id);
while (father != null)
{
Console.Write("=> {0} ", father.Id);
father = father.Father;
}
Console.WriteLine();
}
public void Run(int maxGenerations)
{
Console.WriteLine("Starting simulation");
m_startTime = DateTime.Now;
for (int i = 0; i < maxGenerations; i++)
{
CalculateTheFitnessOfTheCitizens();
SortThePopulationByFitness();
var fittest = m_population[0];
Console.WriteLine("[Gen {0:D5}] {1}", i, fittest);
// Game over...
if (fittest.Fitness == 0)
{
m_endTime = DateTime.Now;
Console.WriteLine();
Console.WriteLine("Perfection reached after {0} generations. Time elapsed: {1}", i, (m_endTime - m_startTime).ToString());
Console.WriteLine();
PrintPaternalLineage(fittest);
break;
}
RemoveTheWeakestCitizens();
MateThePopulation();
}
}
}
class Program
{
static void Main(string[] args)
{
Simulation s = new Simulation(
target: "Genetic algorithms are cool!",
populationSize: 2048,
mutationRate: 0.25);
s.Run(maxGenerations: 100);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
@shaunhey
Copy link
Author

Example run:

Target: Genetic algorithms are cool!, Population Size: 2048, Mutation Rate: 0.25
Starting simulation
[Gen 00000] Id: 000868, Value: QfMNtjc lf!pnMXjp%A1eg;vfuup, Fitness: 0544, Mother: 000000, Father: 000000
[Gen 00001] Id: 003582, Value: <c||sMlhch[uvb0u;o!E^u._oaQF, Fitness: 0491, Mother: 001110, Father: 001054
[Gen 00002] Id: 004920, Value: ERxhvEI,~]Zmzjk~sn$bU8']DF@K, Fitness: 0482, Mother: 003626, Father: 003237
[Gen 00003] Id: 006522, Value: BFqalac 2]nFnd{hh?!_ot1uWW0%, Fitness: 0410, Mother: 004598, Father: 004409
[Gen 00004] Id: 010063, Value: Rgnsv\cTOqqozltipm<tTV6{`UlE, Fitness: 0363, Mother: 007564, Father: 008092
[Gen 00005] Id: 010879, Value: Tshn{gc:cth@zngcos,df`$4aVx7, Fitness: 0324, Mother: 009112, Father: 010173
[Gen 00006] Id: 013758, Value: >mwf]|e"Ukugi{vnY~*Nfh6cxhS1, Fitness: 0297, Mother: 010549, Father: 010612
[Gen 00007] Id: 015929, Value: [nvjvib)1o]w|jgllu'fP`,^ssp*, Fitness: 0243, Mother: 012697, Father: 013940
[Gen 00008] Id: 018245, Value: fc[fosh/\ksmilvnht*lok"s_hi&, Fitness: 0213, Mother: 015543, Father: 015361
[Gen 00009] Id: 020083, Value: EiqDtka8`if{zg|Skt!dkk Uwmh5, Fitness: 0194, Mother: 017353, Father: 017263
[Gen 00010] Id: 022266, Value: 'pgfqjl+apjnvgk[sv!^wg"bOlk*, Fitness: 0179, Mother: 020238, Father: 018598
[Gen 00011] Id: 023898, Value: Bigjdkh!gjoakzpfnn)^w](fqna&, Fitness: 0166, Mother: 021615, Father: 021181
[Gen 00012] Id: 025708, Value: Efrbsje(blwblgvibr2fuh-csll2, Fitness: 0141, Mother: 023372, Father: 022868
[Gen 00013] Id: 028655, Value: Eeqfoki1`v`o|qunox)akZ!jopk*, Fitness: 0132, Mother: 025492, Father: 024660
[Gen 00014] Id: 030542, Value: Ihfgsje%glbofgwjYt ^fa binh/, Fitness: 0120, Mother: 027750, Father: 027685
[Gen 00015] Id: 031602, Value: @bl^ric(dokqnjrxpr Zl`$fopq , Fitness: 0100, Mother: 030275, Father: 028968
[Gen 00016] Id: 034730, Value: IZohycb!arjakgpilt!aoj cqlm#, Fitness: 0086, Mother: 032045, Father: 031312
[Gen 00017] Id: 036319, Value: Hhhfjkl)`ljnuht[pr$`u`"conl , Fitness: 0084, Mother: 033394, Father: 034674
[Gen 00018] Id: 037443, Value: Iemfyg\*`ljovirlsm"^re'[olk", Fitness: 0079, Mother: 036057, Father: 036502
[Gen 00019] Id: 039329, Value: Lathrig!alfprjuilq _o`(cqpm', Fitness: 0061, Mother: 038594, Father: 038559
[Gen 00020] Id: 041477, Value: Ehpgyge(_mfnrfvhlr!amj Yonp", Fitness: 0065, Mother: 038986, Father: 039879
[Gen 00021] Id: 043144, Value: Eembvje!bnhlsitiht _re cqnk', Fitness: 0039, Mother: 042927, Father: 042065
[Gen 00022] Id: 046387, Value: Eenbrjd#ck`qriuilr!bmd&comi#, Fitness: 0049, Mother: 043244, Father: 044290
[Gen 00023] Id: 047933, Value: Iiifsid!_igotipflr!bqd cptk , Fitness: 0042, Mother: 045490, Father: 045294
[Gen 00024] Id: 050502, Value: Ihmfmmb alfprjvepr _re cqpi", Fitness: 0040, Mother: 047792, Father: 048399
[Gen 00025] Id: 052124, Value: Gcqftjh dlhksjtelr _rf dopi , Fitness: 0036, Mother: 050466, Father: 050734
[Gen 00026] Id: 054721, Value: Dfpfvka#^lgltivhkr _rd!conm", Fitness: 0036, Mother: 052782, Father: 051715
[Gen 00027] Id: 056422, Value: Geifxie!alipsiuhou!cre cppl , Fitness: 0028, Mother: 053940, Father: 053377
[Gen 00028] Id: 057711, Value: Ggpfqic _lhprjvhlr aod!aonl , Fitness: 0026, Mother: 055928, Father: 056954
[Gen 00029] Id: 060854, Value: Gengsig amgoqjuilu bre bnnk , Fitness: 0021, Mother: 058408, Father: 057745
[Gen 00030] Id: 063337, Value: Gcofujb!amgoqithkr!ere coni", Fitness: 0023, Mother: 061300, Father: 060680
[Gen 00031] Id: 064051, Value: Ggmfsic alipsjsglr!ase aonl , Fitness: 0020, Mother: 061480, Father: 062256
[Gen 00032] Id: 067368, Value: Gfmfvic blhmrjuhlt!ard bopm , Fitness: 0019, Mother: 064462, Father: 064091
[Gen 00033] Id: 069016, Value: Gfngtja amgmrjuhmq!cre copl , Fitness: 0018, Mother: 066194, Father: 065549
[Gen 00034] Id: 071625, Value: Hclfsic!algorithlr `re cool#, Fitness: 0013, Mother: 067840, Father: 069335
[Gen 00035] Id: 073288, Value: Ghlfsic!blgorjthmr are cool#, Fitness: 0013, Mother: 071625, Father: 069807
[Gen 00036] Id: 075061, Value: Gemgsic algoqithlr are cook , Fitness: 0009, Mother: 071970, Father: 072558
[Gen 00037] Id: 077383, Value: Genesic!akgoqithlr are conk , Fitness: 0009, Mother: 075562, Father: 075061
[Gen 00038] Id: 079730, Value: Fenetib!akgoriuhmr are coql , Fitness: 0009, Mother: 075962, Father: 077749
[Gen 00039] Id: 081255, Value: Genetic!blgprishmr!`re cool , Fitness: 0008, Mother: 077942, Father: 078844
[Gen 00040] Id: 083915, Value: Geoetic!akgorishmr are dool , Fitness: 0007, Mother: 081255, Father: 080300
[Gen 00041] Id: 085471, Value: Gemetic algoriuhlr are cook , Fitness: 0006, Mother: 082934, Father: 082321
[Gen 00042] Id: 086026, Value: Gfnetic!blgorithms bre cool , Fitness: 0005, Mother: 085018, Father: 084292
[Gen 00043] Id: 089818, Value: Genetic algoriuhmr are cool , Fitness: 0003, Mother: 088002, Father: 087478
[Gen 00044] Id: 092066, Value: Genftic algnrithmr are cool , Fitness: 0004, Mother: 088836, Father: 088976
[Gen 00045] Id: 093916, Value: Gemetic algoriuhms are cool , Fitness: 0003, Mother: 091730, Father: 091433
[Gen 00046] Id: 094922, Value: Genetic algorithns are cool", Fitness: 0002, Mother: 092827, Father: 092904
[Gen 00047] Id: 096895, Value: Genetic algorithms are cool , Fitness: 0001, Mother: 095898, Father: 095484
[Gen 00048] Id: 098715, Value: Genetic algorithms are copl , Fitness: 0002, Mother: 097885, Father: 098249
[Gen 00049] Id: 101308, Value: Genetic algorithms are cool , Fitness: 0001, Mother: 098554, Father: 099945
[Gen 00050] Id: 104057, Value: Genetic algorithms are cool , Fitness: 0001, Mother: 100470, Father: 100353
[Gen 00051] Id: 104736, Value: Genetic algorithms are cool", Fitness: 0001, Mother: 104363, Father: 103569
[Gen 00052] Id: 106504, Value: Genetic algorithms are cool!, Fitness: 0000, Mother: 105588, Father: 106076

Perfection reached after 52 generations. Time elapsed: 00:00:01.6859797

106504 => 106076 => 102948 => 102071 => 99331 => 97858 => 95601 => 92374 => 90836 => 89158 => 87579 => 85886 => 81924 => 81102 => 78028 => 76375 => 74983 => 73004 => 70380 => 69520 => 67109 => 65060 => 63273 => 59781 => 58572 => 56695 => 53645 => 51288 => 50721 => 47806 => 46616 => 43620 => 41090 => 40372 => 37620 => 34867 => 34197 => 32485 => 29848 => 28326 => 25414 => 24030 => 20930 => 20135 => 17698 => 16062 => 12859 => 10934 => 8196 => 6880 => 6130 => 3338 => 71
Press any key...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment