Всё началось одним летним вечером, во время чтения книги эволюционного биолога Ричарда Докинза «Бог как иллюзия». Данная книга о религии, вере и атеизме, но автор кратко ссылается на другую книгу «Эгоистичный ген» и вводит одноимённое понятие. Меня долгое время восхищало изящество генетических алгоритмов. И вот, спустя месяц, в очередной раз пытаясь придумать какой-нибудь мини-проект, меня внезапно осенило – а что, если с помощью генетических алгоритмов симулировать эволюцию и посмотреть, что и как будет развиваться. Задача это сложная и на данном этапе развития IT, полагаю, нерешаемая, так что пришлось заняться чем-либо попроще. А именно, проверить гипотезу эгоистичного гена. Заинтересовавшихся, прошу под кат…
Для начала определимся со стандартным представлением эволюции. Согласно википедии: «Биологическая эволюция – естественный процесс развития живой природы, сопровождающийся изменением генетического состава популяций, формированием адаптаций, видообразованием и вымиранием видов, преобразованием экосистем и биосферы в целом». И что важно, единицей эволюции является популяция. Ричард Докинз выдвинул теорию, согласно которой единицей эволюции является не популяция особей какого-либо вида, а сам ген (потому он и назван эгоистичным). И «предназначение» гена не в том, чтобы приспособить особь к окружающим условиям (чтобы она выжила и дала потомство), а сделать всё, чтобы сам ген «выжил». Другим взглядом на данный вопрос являются генетические алгоритмы в программировании — в них «единицей эволюции» признается отдельная особь.
public class World
{
public readonly List<Creature>[] Species = new List<Creature>[8];
public void Run(int generations)
{
for (int i = 0; i < generations; i++, this.age = this.Age + 1)
{
this.SelectBest();
this.MakeChildren();
this.Mutate();
Debug.Print("Age: {0}", i);
}
}
private void SelectBest()
{
var allCreatures = new List<Creature>(this.Species.Sum(kind => kind.Count));
allCreatures.AddRange(this.Species.SelectMany(kind => kind));
allCreatures =
allCreatures.OrderByDescending(creature => creature.SummaryStrength).Take(allCreatures.Count >> 1).ToList();
for (int i = 0; i < this.Species.Length; i++)
{
this.Species[i].ForEach(creature => creature.BreakRedundantConnections());
this.Species[i].Clear();
this.Species[i].AddRange(allCreatures.Where(creature => creature.IdOfSpecies == i));
}
}
private void MakeChildren()
{
Parallel.For(
0,
this.Species.Length,
i =>
{
var temp = new List<Creature>(this.Species[i].Count << 1);
// Random parents (of same species) - for supporting different genes
this.Species[i].Shuffle();
Random rnd = RandomProvider.GetThreadRandom();
for (int j = 1; j < this.Species[i].Count; j += 2)
{
double value = rnd.NextDouble();
if (value < 0.33)
{
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
}
else if (value < 0.665)
{
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
}
else
{
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
temp.Add(new Creature(this.Species[i][j - 1], this.Species[i][j]));
}
}
this.Species[i].ForEach(creature => creature.BreakRedundantConnections());
this.Species[i].Clear();
this.Species[i] = temp;
});
}
private void Mutate()
{
Parallel.ForEach(this.Species, list => list.ForEach(creature => creature.Mutate()));
}
}
public enum Gene : byte
{
SelfishGene = 1,
AltruisticGene = 2,
CreatureLevelGene = 4
}
public enum Relation
{
Child,
BrotherOrSister,
GrandChild,
NephewOrNiece,
Cousin
}
public class Creature
{
private const int GeneStrength = 128;
private readonly Gene[] genes = new Gene[128];
private readonly List<Creature> childs = new List<Creature>(8);
private Creature mother;
private Creature father;
public Creature(int idOfSpecies, World world)
{
Contract.Requires<ArgumentNullException>(world != null);
Contract.Ensures(this.IdOfSpecies == idOfSpecies);
this.IdOfSpecies = idOfSpecies;
this.world = world;
for (int i = 0; i < this.genes.Length; i++)
{
this.genes[i] = EnumHelper.CreateRandomGene();
}
}
public Creature(Creature mommy, Creature daddy)
{
Debug.Assert(mommy.IdOfSpecies == daddy.IdOfSpecies, "Interspecies relation are FORBIDDEN!!!");
this.mother = mommy;
this.father = daddy;
mommy.childs.Add(this);
daddy.childs.Add(this);
this.world = mommy.world;
this.IdOfSpecies = mommy.IdOfSpecies;
for (int i = 0; i < this.genes.Length; i++)
{
this.genes[i] = EnumHelper.ChooseRandomGene(mommy.genes[i], daddy.genes[i]);
}
}
public int SummaryStrength
{
get
{
double sum = 0.0;
World world = this.world;
string cacheKey = $"AltruisticGenesOutStrength{this.IdOfSpecies}";
object cachedValue = Cache.Get(cacheKey, world.Age);
if (cachedValue != null)
{
sum = (double)cachedValue;
}
else
{
for (int i = 0; i < world.Species[this.IdOfSpecies].Count; i++)
{
if (world.Species[this.IdOfSpecies][i] != this)
{
sum += world.Species[this.IdOfSpecies][i].AltruisticGenesOutStrength;
}
}
Cache.Put(cacheKey, world.Age, sum);
}
return this.ThisCreatureGenesStrength + (int)sum + (int)this.HelpFromRelations;
}
}
private int ThisCreatureGenesStrength
=> this.genes.Sum(g => g == Gene.CreatureLevelGene ? GeneStrength : GeneStrength >> 1);
private double AltruisticGenesOutStrength
{
get
{
int sum = 0;
for (int i = 0; i < this.genes.Length; i++)
{
Gene gene = this.genes[i];
if (gene == Gene.AltruisticGene)
{
sum += GeneStrength >> 1;
}
}
return (double)sum / (this.world.Species[this.IdOfSpecies].Count - 1);
}
}
private double HelpFromRelations
{
get
{
Creature mommy = this.mother;
Creature daddy = this.father;
if (mommy == null)
{
return 0;
}
if (mommy.mother == null)
{
return mommy.GetSelfishGenesOutStrength(Relation.Child)
+ daddy.GetSelfishGenesOutStrength(Relation.Child)
+ mommy.childs.Sum(
brother =>
brother == this ? 0 : brother.GetSelfishGenesOutStrength(Relation.BrotherOrSister));
}
return mommy.GetSelfishGenesOutStrength(Relation.Child)
+ daddy.GetSelfishGenesOutStrength(Relation.Child)
+ mommy.childs.Sum(
brother => brother == this ? 0 : brother.GetSelfishGenesOutStrength(Relation.BrotherOrSister))
+ mommy.mother.GetSelfishGenesOutStrength(Relation.GrandChild)
+ mommy.father.GetSelfishGenesOutStrength(Relation.GrandChild)
+ daddy.mother.GetSelfishGenesOutStrength(Relation.GrandChild)
+ daddy.father.GetSelfishGenesOutStrength(Relation.GrandChild)
+ mommy.mother.childs.Sum(
aunt => aunt == mommy ? 0 : aunt.GetSelfishGenesOutStrength(Relation.NephewOrNiece))
+ daddy.mother.childs.Sum(
uncle => uncle == daddy ? 0 : uncle.GetSelfishGenesOutStrength(Relation.NephewOrNiece))
+ mommy.mother.childs.Sum(
aunt =>
aunt == mommy
? 0
: aunt.childs.Sum(cousin => cousin.GetSelfishGenesOutStrength(Relation.Cousin)))
+ daddy.mother.childs.Sum(
uncle =>
uncle == daddy
? 0
: uncle.childs.Sum(cousin => cousin.GetSelfishGenesOutStrength(Relation.Cousin)));
}
}
public void Mutate()
{
// Tries to change 6 genes with 50% probability
int length = this.genes.Length;
int rnd = RandomProvider.GetThreadRandom().Next(length << 1);
int limit = Math.Min(length, rnd + 6);
for (; rnd < limit; rnd++)
{
this.genes[rnd] = EnumHelper.CreateRandomGene();
}
}
public void BreakRedundantConnections()
{
Creature mommy = this.mother;
Creature daddy = this.father;
if (mommy?.mother?.mother != null)
{
mommy.mother.mother?.childs.Clear();
mommy.mother.mother = null;
mommy.mother.father?.childs.Clear();
mommy.mother.father = null;
mommy.father.mother?.childs.Clear();
mommy.father.mother = null;
mommy.father.father?.childs.Clear();
mommy.father.father = null;
daddy.mother.mother?.childs.Clear();
daddy.mother.mother = null;
daddy.mother.father?.childs.Clear();
daddy.mother.father = null;
daddy.father.mother?.childs.Clear();
daddy.father.mother = null;
daddy.father.father?.childs.Clear();
daddy.father.father = null;
}
}
private double GetSelfishGenesOutStrength(Relation whoAreYou)
{
Creature mommy = this.mother;
Creature daddy = this.father;
int summarySelfishStrength = this.genes.Sum(g => g == Gene.SelfishGene ? GeneStrength >> 1 : 0);
switch (whoAreYou)
{
case Relation.Child:
return summarySelfishStrength / this.childs.Count * 30.78;
case Relation.BrotherOrSister:
Debug.Assert(mommy.childs.Count > 1, "LIER! He is not our brother!");
return summarySelfishStrength / (mommy.childs.Count - 1) * 30.78;
case Relation.GrandChild:
return summarySelfishStrength / this.childs.Sum(creature => creature.childs.Count) * 15.38;
case Relation.NephewOrNiece:
Debug.Assert(mommy.childs.Count > 1, "LIER! We don't have any brothers!");
return summarySelfishStrength
/ mommy.childs.Sum(brother => brother == this ? 0 : brother.childs.Count) * 15.38;
case Relation.Cousin:
return summarySelfishStrength
/ (mommy.mother.childs.Sum(aunt => aunt == mommy ? 0 : aunt.childs.Count)
+ daddy.mother.childs.Sum(uncle => uncle == daddy ? 0 : uncle.childs.Count)) * 7.68;
default:
throw new NotImplementedException("Unknown enum value");
}
}
}
К сожалению, не доступен сервер mySQL