Я построил генетический алгоритм, но я чувствую, что что-то не так в части выбора / мутации моего кода. Это та часть кода, о которой я говорю:
#include "stdafx.h"#include <iostream>
#include <vector>
#include <random>
#include <string>
#include <iomanip>
#include <math.h>
// The random number generator I am using.
std::random_device rd;
std::mt19937 rng(rd());
for (int k = 1; k < population_size; k++) // Loop until new population is filled up. K = 1 because first individual has the best genes from last generation.
{
// Calculate total fitness.
double totalfitness = 0;
for (int i = 0; i < population_size; i++)
{
totalfitness += individuals[i].fitness;
}
// Calculate relative fitness.
for (int i = 0; i < population_size; i++)
{
individuals[i].probability = individuals[i].fitness / totalfitness;
}
std::uniform_real_distribution<double> dist2(0.0, 1.0); // Initiate random number generator to generate a double between 0 and 1.
double rndNumber = dist2(rng); // Generate first double
double rndNumber2 = dist2(rng); // Generate second double
double offset = 0.0; // Set offset (starting point from which it'll add up probabilities) at 0.
int father = 0; // father is the individual that is picked, initialize at 0.
int mother = 0;
// Pick first parent. Once picked, set the fitness for that individual at 0 so that it can not be picked again.
for (int i = 0; i < population_size; i++)
{
offset += individuals[i].probability;
if (rndNumber < offset)
{
father = i;
individuals[i].fitness = 0.0;
break;
}
}
offset = 0.0; // Reset offset to zero because we'll start again for the second parent.
totalfitness = 0; // Recalculate total fitness using only the remaining individuals and reset total fitness to 0
// Here we recalculate total fitness using only the fitness of the individuals remaining.
for (int i = 0; i < population_size; i++)
{
totalfitness += individuals[i].fitness;
}
// Then we recalculate probability for the individuals based on the new totalfitness.
for (int i = 0; i < population_size; i++)
{
individuals[i].probability = individuals[i].fitness / totalfitness;
}
// Then we give back the old fitness to the father/mother
individuals[father].fitness = 1 / (individuals[father].evaluation*individuals[father].evaluation);
// Then pick parent 2.
for (int i = 0; i < population_size; i++)
{
offset += individuals[i].probability;
if (rndNumber2 < offset)
{
mother = i;
break;
}
}
// Having picked father and mother, now the idea is to run a random number generator between 0 and 1 for each gene.
// So if: father {5, 8, 9, 3}
// mother {1, 5, 2, 6)
// rndnum {0, 0, 1, 1}
// then child {5, 8, 2, 6}
std::uniform_int_distribution<int> gene_selection(0, 1); // Initiate random number generator to generate an integer between 0 and 1.
for (int i = 0; i < number_of_variables; i++)
{
int gene1 = gene_selection(rng);
if (gene1 == 0)
{
new_individuals[k].chromosomes[0].push_back(individuals[father].chromosomes[0].at(i));
}
else if (gene1 == 1)
{
new_individuals[k].chromosomes[0].push_back(individuals[mother].chromosomes[0].at(i));
}
}
for (int j = 0; j < number_of_variables; j++)
{
for (int l = 0; l < 32; l++)
{
std::uniform_int_distribution<int> mutation(0, 50);
int mutation_outcome = mutation(rng);
if (mutation_outcome == 1)
{
new_individuals[k].chromosomes[0].at(j) ^= (1 << l);
if (new_individuals[k].chromosomes[0].at(j) == 0)
{
int new_var = uni(rng);
new_individuals[k].chromosomes[0].at(j) = new_var;
}
}
}
}
}
// When all new individuals have values, give individuals values of new_individuals and start next round of evaluation.
for (int i = 0; i < population_size; i++)
{
individuals[i] = new_individuals[i];
}
Мой код в основном работает нормально. Похоже, я не могу понять, почему он работает хуже. Первые несколько поколений, кажется, находят новые, лучшие решения довольно часто. Через несколько поколений он перестает находить новые лучшие решения.
Конечно, это может быть из-за того, что лучшего решения не существует, но я также делаю вычисления в Excel одновременно, и человек часто может получить лучшую физическую форму, просто увеличив одну из своих «хромосом» на 1, что обычно изменение на 1 бит, и так как я обычно запускаю этот код с 10000 индивидами, вы бы сказали, что программа обязательно создаст индивида с этой мутацией.
Я много раз перебирал свой код с помощью отладчика, отображая значения на каждом этапе пути и т. Д., Но я не могу понять, где он идет не так, поэтому я решил опубликовать свой код здесь и посмотрим, сможет ли кто-нибудь определить, где я облажался.
Просто для записи, алгоритм просто решатель формул. Таким образом, я могу, например, ввести a = 1, b = 6, target = 50, a * gene1 + b * gene2, и это (теоретически) назначит более высокую пригодность, чем ближе человек будет к получению этого результата.
Кроме того, если бы мне пришлось угадывать, где я все испортил, я бы сказал, что это часть мутации кода:
for (int j = 0; j < number_of_variables; j++)
{
for (int l = 0; l < 32; l++)
{
std::uniform_int_distribution<int> mutation(0, 50);
int mutation_outcome = mutation(rng);
if (mutation_outcome == 1)
{
new_individuals[k].chromosomes[0].at(j) ^= (1 << l);
if (new_individuals[k].chromosomes[0].at(j) == 0)
{
int new_var = uni(rng);
new_individuals[k].chromosomes[0].at(j) = new_var;
}
}
}
}
Я говорю это просто потому, что это та часть, которую я меньше всего понимаю, и я могу себе представить, что я допустил там «невидимую» ошибку.
Во всяком случае, любая помощь будет высоко ценится.
Ну, это просто способ сделать ваш код лучше и эффективнее. Ты используешь std::uniform_int_distribution
без посева и почти с 5 последовательными вызовами, может быть поэтомуour random number is not really random after all
,
Один простой способ to get things better
, было бы seeding the random engine with time
таким образом, предоставляя лучшее создание случайных чисел в долгосрочной перспективе (10000 человек, как-то больших!).
Вот ссылка на сайт для лучшего объяснения этого и простого фрагмента кода для проверки, является следующее:
#include <iostream>
#include <random>
std::default_random_engine generator((unsigned int)time(0));
int random(int n) {
std::uniform_int_distribution<int> distribution(0, n);
return distribution(generator);
}
int main() {
for(int i = 0; i < 15; ++i)
std::cout << random(5) << " " << random(5)<< std::endl;
return 0;
}
Надеюсь, что это поможет! Ура,
Других решений пока нет …