Курсовая работа: Генетические алгоритмы в задаче оптимизации действительных параметров
}
return true;
class CDiophantine
public:
CDiophantine(int, int, int, int, int); // Конструктор с коэффициентами при а,b,c,d
int Solve(); // Вычисляет решение уравнения
gene GetGene(int i)
{ return population[i];}
protected:
int ca,cb,cc,cd; // Коэффициенты при а,b,c,d
int result;
gene population[MAXPOP]; // Массив из генов - популяция
int Fitness(gene &); // Функция приспособленности
void GenerateLikelihoods(); // Вычисляет вероятности воспроизведения
float MultInv();
int CreateFitnesses();
void CreateNewPopulation();
int GetIndex(float val);
gene Breed(int p1, int p2);
Существуют две структуры: gene и класс CDiophantine. gene используется для слежения за различными наборами решений. Создаваемая популяция - популяция ген. Эта генетическая структура отслеживает свои коэффициенты выживаемости и вероятность оказаться родителем. Также есть небольшая функция проверки на равенство, просто чтобы сделать кое-какой другой код покороче. Теперь по функциям:
5.2 Функция Fitness
Вычисляет коэффициент выживаемости (приспособленности - fitness) каждого гена. В нашем случае это - модуль разности между желаемым результатом и полученным значением. Этот класс использует две функции: первая вычисляет все коэффициенты, а вторая - поменьше (желательно сделать ее inline) вычисляет коэффициент для какого-то одного гена.
CDiophantine::Fitness(gene &gn) //Вычисляет коэффициет приспособленности для данного гена
{
int total = ca * gn.alleles[0] + cb * gn.alleles[1] + cc * gn.alleles[2] + cd * gn.alleles[3];
return gn.fitness = abs(total - result);
}
int CDiophantine::CreateFitnesses() // Возвращает номер гена в популяции ,
{ // кот. явл. решением данного ур-я
float avgfit = 0;
int fitness = 0;
for(int i=0;i<MAXPOP;i++)
{
fitness = Fitness(population[i]);
// avgfit += fitness;
if (fitness == 0)
{
return i;
}
}
return 0; //Возвращает 0 ,если среди генов данной популяции не нашлось решения
}
Заметим, что если fitness = 0, то найдено решение - возврат. После вычисления приспособленности (fitness) нам нужно вычислить вероятность выбора этого гена в качестве родительского.
5.3 Функция Likelihood
Как и было объяснено, вероятность вычисляется как сумма обращенных коэффициентов, деленная на величину, обратную к коэффициенту данному значению. Вероятности кумулятивны (складываются), что делает очень легким вычисления с родителями. Например:
Таблица
Хромосома | Вероятность |
1 | (1/84)/0.135266 = 8.80% |
2 | (1/24)/0.135266 = 30.8% |
3 | (1/26)/0.135266 = 28.4% |
4 | (1/133)/0.135266 = 5.56% |
5 | (1/28)/0.135266 = 26.4% |
В программе, при одинаковых начальных значениях, вероятности сложатся: представьте их в виде кусков пирога. Первый ген - от 0 до 8.80%, следующий идет до 39.6% (так как он начинает 8.8). Таблица вероятностей будет выглядеть приблизительно так:
Таблица
Хромосома | Вероятность (smi = 0.135266) |
1 | (1/84)/smi = 8.80% |
2 | (1/24)/smi = 39.6% (30.8+8.8) |
3 | (1/26)/smi = 68% (28.4+39.6) |
4 | (1/133)/smi = 73.56% (5.56+68) |
5 | (1/28)/smi = 99.96% (26.4+73.56) |
Последнее значение всегда будет 100. Имея в нашем арсенале теорию, посмотрим на код. Он очень прост: преобразование к float необходимо для того, чтобы избежать целочисленного деления. Есть две функции: одна вычисляет smi, а другая генерирует вероятности оказаться родителем.
float CDiophantine::MultInv()
{
float sum = 0;
for(int i=0;i<MAXPOP;i++)
{
sum += 1/((float)population[i].fitness);
}
return sum; //Сумма обратных коэффициентов приспособленности всех генов в популяции
}
void CDiophantine::GenerateLikelihoods() //Генерирует вероятности воспроизведения
{
float multinv = MultInv();
float last = 0;
for(int i=0;i<MAXPOP;i++)
{
population[i].likelihood = last = last + ((1/((float)population[i].fitness) / multinv) * 100);
}
}
Итак, у нас есть и коэффициенты выживаемости (fitness) и необходимые вероятности (likelihood). Можно переходить к размножению (breeding).
5.4 Функции Breeding
Функции размножения состоят из трех: получить индекс гена, отвечающего случайному числу от 1 до 100, непосредственно вычислить кроссовер двух генов и главной функции генерации нового поколения. Рассмотрим все эти функции одновременно и то, как они друг друга вызывают. Вот главная функция размножения:
CDiophantine::CreateNewPopulation() {
gene temppop[MAXPOP];
for(int i=0;i<MAXPOP;i++)
{
int parent1 = 0,parent2 = 0, iterations = 0;
while(parent1 == parent2 || population[parent1] == population[parent2])
{
parent1 = GetIndex((float)(rand() % 101));
parent2 = GetIndex((float)(rand() % 101));
if (++iterations > MAXPOP) break; // В этом случае родителей выбираем случайно
}
temppop[i] = Breed(parent1, parent2); // Процесс размножения
}
for(i=0;i<MAXPOP;i++)
population[i] = temppop[i];
}
Итак, первым делом мы создаем случайную популяцию генов. Затем делаем цикл по всем генам. Выбирая гены, мы не хотим, чтобы они оказались одинаковы (ни к чему скрещиваться с самим собой :), и вообще - нам не нужны одинаковые гены (operator = в gene). При выборе родителя, генерируем случайное число, а затем вызываем GetIndex. GetIndex использует идею куммулятивности вероятностей (likelihoods), она просто делает итерации по всем генам, пока не найден ген, содержащий число:
int CDiophantine::GetIndex(float val) // По данному числу от 0 до //100 возвращает
{ // номер необходимого гена в популяции
float last = 0;
for(int i=0;i<MAXPOP;i++)
{
if (last <= val && val <= population[i].likelihood) return i;
else last = population[i].likelihood;
}
return 4;
}
Возвращаясь к функции CreateNewPopulation(): если число итераций превосходит MAXPOP, она выберет любых родителей. После того, как родители выбраны, они скрещиваются: их индексы передаются вверх на функцию размножения (Breed). Breed function возвращает ген, который помещается во временную популяцию. Вот код:
gene CDiophantine::Breed(int p1, int p2)
{
int crossover = rand() % 3+1; //Число от 1 до 3 - генерируем точку кроссовера.
int first = rand() % 100; // Какой родитель будет первым ?
gene child = population[p1]; // Инициализировать ребенка первым родителем
int initial = 0, final = 4;
if (first < 50) initial = crossover;
// Если первый родитель р1(вероятность этого 50%) -
// начинаем с точки кроссовера
else final = crossover; // Иначе - заканчиваем на точке кроссовера
for(int i=initial;i<final;i++)
{ // Кроссовер
child.alleles[i] = population[p2].alleles[i];
if (rand() % 101 < 5) child.alleles[i] = rand() % (result + 1);
// 5% - мутация
}
return child; // Возвращаем ребенка
}
void CDiophantine::CreateNewPopulation() {
gene temppop[MAXPOP];
for(int i=0;i<MAXPOP;i++)
{
int parent1 = 0,parent2 = 0, iterations = 0;
while(parent1 == parent2 || population[parent1] == population[parent2])
{
parent1 = GetIndex((float)(rand() % 101));
parent2 = GetIndex((float)(rand() % 101));
if (++iterations > MAXPOP) break; // В этом случае родителей выбираем случайно
}
temppop[i] = Breed(parent1, parent2); // Процесс размножения
}
for(i=0;i<MAXPOP;i++)
population[i] = temppop[i];
}
В конце концов мы определим точку кросс-овера. Заметим, что мы нехотим, чтобы кросс-овер состоял из копирования только одного родителя. Сгенерируем случайное число, которое определит наш кроссовер. Остальное понятно и очевидно. Добавлена маленькая мутация, влияющая на скрещивание. 5% - вероятность появления нового числа.
Страницы: 1, 2, 3, 4, 5, 6, 7, 8