Genetic drift — along with natural selection, mutation, and migration — is one of the basic mechanisms of evolution.
In each generation, some individuals may, just by chance, leave behind a few more descendents (and genes, of course!) than other individuals. The genes of the next generation will be the genes of the "lucky" individuals, not necessarily the healthier or "better" individuals. That, in a nutshell, is genetic drift. It happens to ALL populations — there's no avoiding the vagaries of chance.
The following code calculates a pseudo-random likelyhood of event-sequences as part of Genetic drift. I know Python would probably be a better choice, and fit more with the console iteraction taking place, but, that's probably a question for another day.
It takes input in the form of an optional config array with three (combinationally optional) parameters:
gen
: the amount of generations to execute (int
input, orcontinuous
)population
: a dictionary of populations, can look like:{'Red Beetle': 50, 'Blue Beetle': 50, 'Green Beetle': 0, 'Purple Beetle': 50, 'Orange Beetle': 50}
{red: 50, blue: 50}
- Or different combinations
draw_amount
: the amount of population to test for the next generation
The logic follows a similar pattern to this:
- Until either the generations result in a single population surviving or the counter is hit, continue looping over the following: (loop here)
- take a dictionary form like
{red: 2, blue: 2}
and turn it into['red', 'red', 'blue', 'blue]
(add thekey
to an arrayvalue
times) (double loop here)- uses
Math.random
to fetchdraw_amount
values from the recently populated array (loop here)- Prints result (loop here)
- Converts that back into a dictionary (loop here)
I was worried about a few things:
- Too many loops?
- When converting back into an array, with a
draw_amount
that doesn't getmod 0
with thepopulation
- Whether the error handlers have been correctly used
var genetic_drift = function(user_config){
var config = {};
var default_config = {
gen: 5,
population: {red: 50, blue: 50},
draw_amount: 10
};
(function(user_config){
if (user_config){
for (var item in default_config){
user_config[item] ? '' : user_config[item] = default_config[item];
}
} else {
user_config = default_config;
}
config = user_config;
})(user_config);
genetic_drift.begin = function(){
var population = [];
var ratio_population = config.population;
var until = (config.gen == 'continuous');
for (var i = 0, gen = (until ? 1 : config.gen); i < gen; i++){
population = [];
if (Object.keys(ratio_population).length == 1){
console.log('Generation ' + i + ': One trait left: ' + Object.keys(ratio_population)[0]);
break;
}
for (var item in ratio_population){
for (var k = 0; k < ratio_population[item]; k++){
population.push(item);
}
}
if (config.draw_amount >= population.length){
throw ('SimulationError: The DrawAmount is more than the Population.');
}
ratio_population = process_generation(population, config.draw_amount, i+1);
if (until){
gen++;
}
}
};
function process_generation(population, draw_amount, gen){
var temp_drawn = [];
var temp_final = {};
for (var i = 0; i < draw_amount; i++){
temp_drawn.push(population[Math.floor(Math.random() * population.length)]);
}
if (population.length % draw_amount == 0){
temp_drawn.forEach(function(item){
for (var i = 0, multiplier = Math.floor(population.length / draw_amount); i < multiplier; i++){
temp_final[item] = (item in temp_final ? temp_final[item] + 1 : 1);
}
});
}
var print_result = "Generation " + gen + ": ";
Object.keys(temp_final).forEach(function(key){
print_result += [key, temp_final[key]].join(':') + ' '
});
console.log(print_result);
return temp_final;
};
};
Which then would be used like:
var config = { gen: 'continuous', population: { 'Red Beetle': 50, 'Blue Beetle': 50, 'Green Beetle': 0, 'Purple Beetle': 50, 'Orange Beetle': 50 }, draw_amount: 20 }; genetic_drift(config); genetic_drift.begin();
Resulting in something like this:
Generation 1: Orange Beetle:70 Blue Beetle:70 Red Beetle:30 Purple Beetle:30 Generation 2: Orange Beetle:80 Purple Beetle:10 Red Beetle:40 Blue Beetle:70 Generation 3: Blue Beetle:60 Red Beetle:30 Orange Beetle:110 Generation 4: Red Beetle:30 Orange Beetle:110 Blue Beetle:60 Generation 5: Orange Beetle:120 Red Beetle:30 Blue Beetle:50 Generation 6: Orange Beetle:150 Red Beetle:40 Blue Beetle:10 Generation 7: Orange Beetle:150 Red Beetle:50 Generation 8: Orange Beetle:180 Red Beetle:20 Generation 9: Orange Beetle:180 Red Beetle:20 Generation 10: Orange Beetle:200 Generation 10: One trait left: Orange Beetle