Введение


Модели естественных вычислений широко применяются в современной науке. Область их применения очень обширна, они используются для решения задач моделирования, искусственного интеллекта, распознавания образов, управления.

Одним из наиболее распространенных методов естественных вычислений являются генетические алгоритмы. Чтобы лучше разобраться, как эти алгоритмы устроены и как работают, было решно воспроизвести один из таких алгоритмов — генетический. Для того, чтобы применять какой-либо метод для решения конкретных задач этот метод необходимо освоить. Поэтому генетический алгоритм, рассмотренный в данной работе, не решает никакой конкретной задачи. Главными являются одновременно процесс и результат работы по созданию программы по моделированию и визуализации работы генетического алгоритма. Важен полученный программистский опыт.
Программа моделирует поведение популяции самых примитивных живых организмов. Эта программа вряд ли будет иметь какое-либо практическое применение, но она наглядно иллюстрирует принцип работы генетических алгоритмов.

Моделирование работы генетического алгоритма, в котором естественный отбор определяется условиями среды


Моделирование – метод научного познания объективного мира через построение и изучение моделей.

Визуализация – один из наиболее удобных для человека способов представления информации. Человеку удобнее воспринимать информацию, если она представлена графически, а не в виде большого массива ничего не значащих чисел, поэтому важной частью работы является графическое представление алгоритма.

Прежде чем использовать какой-либо метод, его нужно изучить и апробировать сначала на относительно простой задаче возможно несколько раз. Для программиста таким изучением является написание конкретных программ.

Для работы выбран язык программирования C++, так как этот язык является мощным, проверенным временем языком программирования. C++ получил широкое распространение среди программистов. Для визуализации использована открытая графическая библиотека OpenGL.

Цель и задачи

Цель работы — написать программу для моделирования работы и визуализации генетического алгоритма, в котором естественный отбор определяется в зависимости от условий среды.
Задачи
  1. Провести анализ источников информации.
  2. Изучить основные понятия, связанные с генетическими алгоритмами
  3. Создать биологический вид примитивных живых организмов.
  4. Создать систему взаимодействия организмов друг с другом.
  5. Организовать жизненный цикл (рождение новых организмов, продолжительность жизни, размножение и смерть).
  6. Создать движущий фактор эволюции.
  7. Организовать отношения хищник-жертва.
  8. В заключение нужно реализовать сбор статистических данных о популяции.

Создание вида

Программа написана на языке программирования C++ с использованием открытой графической библиотеки OpenGL.
Для представления биологического вида создан класс Bio, который включает в себя координаты, скорость, время жизни и другие параметры примитивного живого организма.

Поля класса Bio
public:
	Code_bio code; //генетический код
	float x,y,dx,dy; //координаты и скорость
	bool life; //жив или мертв
	float w,h; //линейные размеры
	int num; //личный отличительный номер 
	int lifetime; //время жизни
	float range; //радиус обнаружения хищника

В классе Bio предусмотрен конструктор (для создания объектов класса).
Bio(void)
{
	life=1; //оживление
	w=20; //размер по умолчанию
	h=20;
	num=rand()%10000;
	range=200;
	//srand(time(0)); //случайные координаты
	x=rand()%(winW-(int)w);
	y=rand()%(winH-(int)h);
	//srand(time(0));
	dx=rand()%8-4; //случайная скорость
	dy=rand()%8-4;
	lifetime=0;
}


Помимо конструктора необходимо создать функцию set(int a, int b), с помощью которой можно будет вручную задавать координаты создаваемого объекта.
void set(int a,int b){
	x=a;
	y=b;
	life=true;
	dx=rand()%8-4;
	dy=rand()%8-4;
	}


Для имитации случайного движения вводим функцию turn(), которая будет в каждый момент времени поворачивать объект на случайный угол влево или вправо.
inline void turn(float vect, int deg)
{
	if((dx!=0)||(dy!=0))
	{
		float grad;
		if((dx>=0)&&(dy<0))
		{
			dy=-dy;
			grad=atan(dx/dy)*180/pi;
			grad+=deg;
			dx=sin(grad*pi/180)*vect;
			dy=cos(grad*pi/180)*vect;
			dy=-dy;
		}
		if((dx>=0)&&(dy>=0))
		{
			grad=atan(dx/dy)*180/pi;
			grad=90-grad;
			grad+=deg;
			grad=90-grad;
			dx=sin(grad*pi/180)*vect;
			dy=cos(grad*pi/180)*vect;
		}
		if((dx<0)&&(dy<=0))
		{
			dy=-dy;
			dx=-dx;
			grad=atan(dx/dy)*180/pi;
			grad=90-grad;
			grad+=deg;
			grad=90-grad;
			dx=sin(grad*pi/180)*vect;
			dy=cos(grad*pi/180)*vect;
			dy=-dy;
			dx=-dx;
		}
		if((dx<0)&&(dy>=0))
			{
				dx=-dx;
				grad=atan(dx/dy)*180/pi;
				grad+=deg;
				dx=sin(grad*pi/180)*vect;
				dy=cos(grad*pi/180)*vect;
				dx=-dx;
			}
		}
	}


Для обработки столкновения объекта с краями экрана нужны функции collx() и colly().
inline void collx()
	{
		if(x-w/2<0)
		{
			x=winW-w/2;
		}
		if(x+w/2>winW)
		{
			x=w/2;
		}
	}

	inline void colly()
	{
		if(y-h/2<0)
		{
			y=h/2;
			dy=-dy;
		}
		if(y+h/2>winH)
		{
			y=winH-h/2;
			dy=-dy;
		}
	}
};


Теперь нужна только главная функция Draw(), которая будет изменять состояние объекта и отрисовывать его.
void Bio::Draw()
{
	if(life)
	{
		if(dx==0)dx=rand()%30/10+1;
		if(dy==0)dy=rand()%30/10+1;
		//УБЕГАЙ
		float min=range;
		bool ok=0;
		float x2,y2;
		list<Predator>::iterator i=preds.begin();
		for(;i!=preds.end();i++)
		{
			if(sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) )<min)
			{
				min=sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) );
				ok=1;
				x2=i->x;
				y2=i->y;
			}
		}
		if(ok)
		{
			runaway(code.maxspeed,x2,y2);
			turn(code.maxspeed,(rand()%(2*(int)code.maxturn+1)*10)/10-(int)code.maxturn);
		}
		else
		turn((rand()%(int)(code.maxspeed-code.minspeed+1)*10)/10+code.minspeed,
			(rand()%(2*(int)code.maxturn+1)*10)/10-(int)code.maxturn);
			x+=dx;
		collx();
		y+=dy;
		colly();
		//ОТРИСОВКА
		if(1)
		{
		        glBindTexture(GL_TEXTURE_2D,bio_tex[0]);
		        glBegin(GL_QUADS);
			glTexCoord2f(0,1); glVertex2f(x-w/2,y-h/2);
				glTexCoord2f(1,1); glVertex2f(x+w/2,y-h/2);
				glTexCoord2f(1,0); glVertex2f(x+w/2,y+h/2);
				glTexCoord2f(0,0); glVertex2f(x-w/2,y+h/2);
			glEnd();
		}
		//ДЕТИ
		makechild();
		//ПРОД. ЖИЗНИ
		lifetime++;
		if(lifetime>code.maxltime)
			life=0;
		//РОСТ
		w=lifetime/40+20;
		h=lifetime/40+20;
	}
}

На этом создание вида заканчивается. Теперь встает вопрос о хранении всех этих организмов (их ведь будет большое количество). Их количество также будет постоянно изменяться в результате действия жизненного цикла. Для их хранения хорошо подойдет динамический массив list.
list<Bio> bios;


Жизненный цикл

В результате первая задача выполнена. Далее нужно организовать жизненный цикл популяции и взаимодействие организмов друг с другом. Поскольку программа должна моделировать поведение самых примитивных организмов, то и взаимоотношения их будут самыми простыми. При столкновении двух особей, они будут умирать, и в случайных координатах будут появляться их дети (от 1 до 3). Учитывая вычислительные ресурсы обычного компьютера, программа сможет с нормальной скоростью обрабатывать до 200 организмов. Это количество недостаточно для существования стабильной популяции, поэтому чтобы число организмов не становилось слишком большим или слишком маленьким введено условие – если численность популяции меньше 50, рождается больше детей (от 1 до 4), если численность больше 50, размножение будет медленнее.

Для того чтобы генетический алгоритм работал, у каждой особи популяции должен быть свой генетический код. Для этого в программе создана отдельная структура кода. Каждый организм популяции имеет свой экземпляр этой структуры. Также необходима функция скрещивания, которая будет принимать генетические коды родителей и возвращать код ребенка. У этих примитивных существ в коде заложены только минимальная и максимальная скорости, максимальная продолжительность жизни и максимальный угол поворота.
struct Code_bio
{
	int maxltime;
	float minspeed,maxspeed;
	int maxturn;
};

inline Code_bio childcode(Code_bio c1, Code_bio c2)
{
	//СКРЕЩИВАНИЕ
	Code_bio c;
	c.maxltime=(c1.maxltime+c2.maxltime)/2;
	c.minspeed=(c1.minspeed+c2.minspeed)/2;
	c.maxspeed=(c1.maxspeed+c2.maxspeed)/2;
	c.maxturn=(c1.maxturn+c2.maxturn)/2;
	
	//МУТАЦИЯ
	c.maxltime+=c.maxltime/100*((rand()%(maxltime_mut*2+1))-maxltime_mut);
	c.maxspeed+=c.maxspeed/100*((rand()%(maxspeed_mut*2+1))-maxspeed_mut);
	c.minspeed+=c.minspeed/100*((rand()%(minspeed_mut*2+1))-minspeed_mut);
	c.maxturn+=c.maxturn/100*(rand()%(maxturn_mut*2+1)-maxturn_mut);
        return c;
}

В класс Bio теперь нужно добавить функцию размножения. Эта функция будет обнаруживать столкновения организмов, убивать их и создавать потомство.
void Bio::makechild()
{
	list<Bio>::iterator i=bios.begin();
	for(;i!=bios.end();i++)
	{
		if(num!=i->num)
		{
		if((lifetime>200)&&(i->lifetime>200))
		if(sqrt((x-i->x)*(x-i->x))+((y-i->y)*(y-i->y))<w+i->w)
			{
				life=0;
				i->life=0;
				int c;
				if(bios.size()<50)
					c=rand()%4+1;
				else c=rand()%3+1;
				for(int j=0;j<c;j++)
				{
					Bio b;
					b.code=childcode(code,i->code);
					bios.push_back(b);
				}
			}
		}
	}
}

В функцию отрисовки нужно тоже внести изменение: добавить размножение. Отрисовка всех объектов происходит в функции таймера с интервалом 20 мс. В этой же функции программа удаляет все мертвые особи (значение life которых равно false).
list<Bio>::iterator i=bios.begin();
for(;i!=bios.end();i++)
	i->Draw();
bios.remove_if([](Bio b) {return (b.life==0);}); //удаление мертвых

Хищник

Поскольку генетический алгоритм все-таки должен решать какую-то задачу, нужно поставить ему эту задачу. В данном случае задача будет заключаться в нахождении наиболее приспособленного организма. Популяция будет все время совершенствоваться в результате эволюции. Каждое следующее поколение будет лучше приспособлено к условиям окружающей среды. Необходимо создать движущий фактор эволюции, который будет отбирать наиболее приспособленные особи. Этим фактором будет хищник.

Хищников, как и жертв, может быть много. Класс хищника от класса Bio будет отличаться только тем, что хищники не будут уметь размножаться и умирать. Их количество будет всегда фиксированным на протяжении работы программы.

Поля класса Predator
public:
	float x,y,dx,dy,speed;
	bool life;
	float w,h;
	int num;
	int lifetime;
	float range;
	float hungry;

Конструктор по умолчанию и функция set() для ручного создания.
Predator(void)
{
	life=1;
	w=100;
	h=100;
	num=rand()%10000;
	range=250;
	speed=10.2;
	hungry=0;//1*1000/20;
	//srand(time(0));
	x=rand()%(winW-(int)w);
	y=rand()%(winH-(int)h);
	//srand(time(0));
	dx=rand()%8-4;
	dy=rand()%8-4;
	lifetime=0;
}

void set(int a,int b)
{
	x=a;
	y=b;
	life=true;
	dx=rand()%8-4;
	dy=rand()%8-4;
}

В свободное от еды время хищник будет совершать случайное движение. Его функция turn() для изменения направления ничем не отличается от аналогичного метода класса Bio. Для того, чтобы хищник мог догонять жертву, ему нужна функция aim(), которая будет принимать координаты жертвы и скорость, с которой надо ее догонять.
inline void aim(float vect, float x2, float y2)
{
	dx=((x2-x)*vect)/sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) );
	dy=((y2-y)*vect)/sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) );
}


В главной функции Draw(), если хищник голоден, он будет просматривать свое окружение, находить самую близкую жертву и гнаться за ней. Если он не голоден, он будет случайно двигаться по экрану.
inline void Draw()
{
	if(life)
	{
		if(dx==0)dx=rand()%30/10+1;
		if(dy==0)dy=rand()%30/10+1;
		//ПОИСК ЕДЫ
		bool ok=0;
		float x2,y2;
		if(hungry<=0)
		{
			float min=range;
			list<Bio>::iterator i=bios.begin();
			for(;i!=bios.end();i++)
			{
			if(sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) )<w/2+i->w/2-10)
				{
					i->life=0;
					hungry=0.01*1000/20;
				}
				else
				if(sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) )<min)
				{
					min=sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) );
					ok=1;
					x2=i->x;
					y2=i->y;
				}
			}
		}
		if(ok)
			aim(speed,x2,y2);
		else turn(6,(rand()%(2*15+1)*10)/10-15);
		x+=dx;
		collx();
		y+=dy;
		colly();
			
		//ОТРИСОВКА
		if(view)
		{
		glBindTexture(GL_TEXTURE_2D,pred_tex[0]);
		glBegin(GL_QUADS);
			glTexCoord2f(0,1); glVertex2f(x-w/2,y-h/2);
			glTexCoord2f(1,1); glVertex2f(x+w/2,y-h/2);
			glTexCoord2f(1,0); glVertex2f(x+w/2,y+h/2);
			glTexCoord2f(0,0); glVertex2f(x-w/2,y+h/2);
		glEnd();
		}
			
		//ПРОД. ЖИЗНИ
			
		//РОСТ
		//ГОЛОД
		hungry--;
	}
}

Осталось только создать динамический массив хищников.
list<Predator> preds;

Отношения хищник-жертва

Для того чтобы взаимоотношения между хищником и жертвой работали правильно, нужно научить жертву убегать от хищника. Иначе хищник будет просто съедать жертву и никакой эволюции происходить не будет. Для этого надо добавить в класс Bio метод runaway(). Он аналогичен методу преследования хищником жертвы, только направляет объект в противоположную сторону.
inline void runaway(float vect, float x2, float y2)
{
	dx=((x-x2)*vect)/sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) );
	dy=((y-y2)*vect)/sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) );
}

Теперь нужно окончательно модифицировать функцию Draw класса Bio. Помимо простого хаотичного движения надо добавить обнаружение хищника и бегство от него.
if(dx==0)dx=rand()%30/10+1;
	if(dy==0)dy=rand()%30/10+1;
	//УБЕГАЙ
	float min=range;
	bool ok=0;
	float x2,y2;
	list<Predator>::iterator i=preds.begin();
	for(;i!=preds.end();i++)
	{
		if(sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) )<min)
		{
			min=sqrt( (x-i->x)*(x-i->x)+(y-i->y)*(y-i->y) );
			ok=1;
			x2=i->x;
			y2=i->y;
		}
	}
	if(ok)
		runaway(code.maxspeed,x2,y2);
	else
	turn((rand()%(int)(code.maxspeed-code.minspeed+1)*10)/10+code.minspeed,
				(rand()%(2*code.maxturn+1)*10)/10-code.maxturn);


Вероятность поимки хищником быстрой жертвы меньше, чем вероятность поимки медленной. В результате этого быстрые особи будут лучше приспособлены к условиям среды, и постепенно будет наблюдаться увеличение средней скорости популяции.

Принцип действия эволюции

Чтобы эволюция заработала, необходимо было внести еще несколько важных моментов. Когда хищник начинает преследовать жертву, жертва убегает от него в противоположную сторону. Если скорость жертвы будет намного меньше скорости хищника, то эволюция происходить не будет, потому что тогда при незначительных эволюционных изменениях ее скорости хищник все равно догонит и съест жертву. Если же скорость жертвы будет больше скорости хищника, он не сможет догнать ее, разве только если загонит в угол. Остается только один вариант: скорость жертвы должна быть приблизительно равна скорости хищника. Тогда даже при самом небольшом эволюционном изменении скорости жертвы, у нее появится существенное преимущество по сравнению с остальными. У хищника будет меньше шансов догнать такую жертву.

Когда программа создает начальную популяцию, она всем особям задает одинаковые генотипы, в которых указаны одинаковые максимальные и минимальные скорости. Все параметры генотипов потомства рассчитываются, как среднее арифметическое параметров родителей. Например, максимальная скорость ребенка будет средним арифметическим средних скоростей родителей. Если генетический алгоритм запустить именно так, эволюция работать не будет. Все генотипы просто усреднятся и станут одинаковыми. Даже если начальную популяцию создать с разными генотипами, все равно эволюции не будет. Кроме естественного отбора и движущего фактора для эволюции нудна мутация. Все что нужно – это после вычисления среднего арифметического изменять его на плюс/минус 10%. Тогда некоторые особи в результате скрещивания будут рождаться быстрее, а некоторые – медленнее, и эволюция будет работать.

Во время работы программа раз в 10 секунд записывает в четыре файла общие показатели популяции: численность, среднюю, минимальную и максимальную скорости. Используя эти данные можно будет увидеть результат работы генетического алгоритма и сделать вывод об эволюции популяции.

Фрагмент собранных данных:

Численность, Мин. Скорость, Макс. Скорость, Ср. скорость:
45,00 1,01842 9,83393 5,42617
54,00 1,01048 10,214 5,61252
53,00 1,00726 10,4374 5,72231
51,00 0,932109 10,1463 5,53921
48,00 0,93236 10,6849 5,80864
45,00 0,908688 11,0295 5,96907
46,00 0,888795 11,1242 6,0065
51,00 0,894669 11,1927 6,04366
48,00 0,933062 11,679 6,30601
52,00 0,92753 11,9278 6,42764
54,00 0,899226 12,3086 6,6039
51,00 0,875113 12,52 6,69756
43,00 0,902515 12,84 6,87124

На основании этих данных выполняется построение графика:

image

На графике видно, что в результате эволюции средняя максимальная скорость всех организмов увеличилась в 10 раз. Это действительно колоссальный скачок эволюции организмов. Из этого графика можно также сделать вывод, что средняя минимальная скорость всех организмов не изменилась. Это связано с особенностями движения существ. В то время когда они не убегают от хищника, они просто бегают по экрану в случайных направлениях. При этом их скорость тоже подвержена случайному изменению. Она принимает случайное значение между их минимально возможной скоростью, заложенной в генотипе, и максимально возможной скоростью, так же заложенной в генотипе. Но когда эти организмы убегают от врага, они бегут со своей максимально возможной скоростью. Именно поэтому движущий фактор эволюции, который производит отбор самых приспособленных особей, изменяет только их максимально возможные скорости, что и показывает график.

Результаты и выводы

  1. Создан искусственный биологический вид для работы генетического алгоритма.
  2. Создана система взаимодействия организмов друг с другом.
  3. Организован жизненный цикл (рождение новых организмов, продолжительность жизни, размножение и смерть).
  4. В качестве движущего фактора эволюции создан хищник.
  5. Организовано взаимодействие хищника и жертвы.
  6. Собран пример статистических данных, который наглядно демонстрирует работу алгоритма и доказывает эволюцию созданной популяции.

Заключение


Созданная программа отвечает всем заявленным требованиям. На первый взгляд не выполняет никакой конкретной общественно-полезной задачи, но она наглядно демонстрирует принцип работы генетического алгоритма. Алгоритм, реализованный в этой программе, довольно прост, но, не поняв, как он работает, нельзя создать более сложный генетический алгоритм. Поэтому главная польза программы – опыт для ее автора и пример исследовательской попытки для таких же любознательных начинающих программистов.

В перспективе предполагается рассмотреть на практике параллельные алгоритмические модели генетических алгоритмов.

Литература

1. Ершов Н. М. Естесственные модели параллельных вычислений [Электронный ресурс] URL: naturalmodels.blogspot.ru (дата обращения 10.05.14).
2. Р. Лафоре «Объектно-ориентированное программирование в C++», 4-е издание, 2011г.
3. Б. И. Пахомов «C/C++ и MS Visual C++ 2012».

Комментарии (26)


  1. Robotofob
    10.08.2015 11:18

    Интересно, на сколько на основе этой работы можно организовать эволюцию кода. Например, есть разные языки программирования (царство), есть программы различного применения (тип), есть программы, решающие примерно одни и те же задачи (класс). Берем все это, и по описанным выше алгоритмам сталкиваем в одной «среде обитания». Следим за размножением, эволюцией и получаем на выходе более совершенные модифицированные решения.


    1. svistunov
      10.08.2015 11:51

      Книга «Programming Collective Intelligence», глава 11


      1. Robotofob
        10.08.2015 12:04

        Спасибо! По содержанию очень похоже на желаемое! Читать!..


  1. ls1
    10.08.2015 12:11
    +5

    А можно

    1. Ссылку на проект
    2. Анимацию процесса где-нибудь посмотреть


  1. QtRoS
    10.08.2015 13:16
    +2

    Хм, чисто по технической стороне замечания:
    1. Почему OpenGL? Рисование простенькое, можно было бы простой GUI библиотечкой обойтись и контролы прикрутить, чтобы всеми параметрами управлять.
    2. По C++ разные недочеты — не используете списки инициализации, используется постфиксную форму инкремента на итераторе, кодинг стайл без дополнительных пробелов между математчиескими операциями ИМХО плохо читается, поля классов никак особенно не именуются, скачет регистр (то caml case, то Pascal), используете лямбды, но не используете auto, а напрашивается. А еще странно выглядит «bool ok=0;». C++ же, можно и true написать.
    3. Ну и скриншотов не хватает, мне кажется.


    1. qw1
      10.08.2015 14:07
      +1

      А еще странно выглядит «bool ok=0;». C++ же, можно и true написать.

      #define true 0?


    1. PHmaster
      10.08.2015 14:10
      +1

      Туда же до кучи можно добавить полнейший хаос в «архитектуре»: взять хотя бы метод Draw, который внезапно не только рисует, но еще и передвигает, размножает, спасает от хищников (не пойми откуда в нем вообще взявшихся).


    1. Ramdeif
      24.08.2015 11:08

      OpenGL я изучаю давно и уже очень привык к нему. Что касается кода, я его писал несколько лет назад и многого еще не знал. А отсутствие скриншотов — это мой косяк.


  1. wrewolf
    10.08.2015 13:36

    Кажется вы читали

    Восход Эндимиона


    1. Ramdeif
      24.08.2015 11:10

      Нет, не читал.


  1. gro
    10.08.2015 13:37
    +6

    Делал много подобных поделий. Это было забавно, но называть это «доказательством эволюции живых организмов» я не стал.


    1. Ramdeif
      24.08.2015 11:10

      Наверное я оказался чуть смелее.


  1. berez
    10.08.2015 17:05

    При столкновении двух особей, они будут умирать, и в случайных координатах будут появляться их дети (от 1 до 3).

    Какое-то странное правило. А если особи не столкнутся, они что — живут вечно?

    inline Code_bio childcode(Code_bio c1, Code_bio c2)
    {
        //СКРЕЩИВАНИЕ
        Code_bio c;
        c.maxltime=(c1.maxltime+c2.maxltime)/2;
        c.minspeed=(c1.minspeed+c2.minspeed)/2;
        c.maxspeed=(c1.maxspeed+c2.maxspeed)/2;
        c.maxturn=(c1.maxturn+c2.maxturn)/2;
        
        //МУТАЦИЯ
        c.maxltime+=c.maxltime/100*((rand()%(maxltime_mut*2+1))-maxltime_mut);
        c.maxspeed+=c.maxspeed/100*((rand()%(maxspeed_mut*2+1))-maxspeed_mut);
        c.minspeed+=c.minspeed/100*((rand()%(minspeed_mut*2+1))-minspeed_mut);
        c.maxturn+=c.maxturn/100*(rand()%(maxturn_mut*2+1)-maxturn_mut);
            return c;
    }
    

    Вы меня простите, конечно, но с генетикой тут нет ничего общего. Это не генетический алгоритм.

    Суть генетических алгоритмов в том, что генетическая информация хранится в родителях в виде битовой строчки. При скрещивании и мутировании тоже используется эта битовая строчка, безо всякого знания о ее внутреннем устройстве. Только потом получившийся код «парсится на гены» (т.е. разбирается на составляющие его поля) и используется при создании нового организма.


    1. mihaild
      11.08.2015 23:37
      +1

      Откуда такая информация про определение генетических алгоритмов?
      Мне всегда казалось, что основной их особенностью является оператор скрещивания, остальное непринципиально.
      (заметим, что в реальных организмах скрещивание происходит как раз на уровне генов, а не нуклеотидов)


      1. berez
        12.08.2015 14:38

        Откуда такая информация про определение генетических алгоритмов?

        ru.wikipedia.org/wiki/%D0%93%D0%B5%D0%BD%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC

        Я когда-то довольно плотно игрался с генетическими алгоритмами. Они отлично работают в задачах, где куча параметров, неочевидно влияющих на результат (и где тупой перебор работал бы веками).

        Мне всегда казалось, что основной их особенностью является оператор скрещивания, остальное непринципиально.
        (заметим, что в реальных организмах скрещивание происходит как раз на уровне генов, а не нуклеотидов)

        Скрещивание происходит вообще на уровне хромосом: ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%BE%D1%81%D1%81%D0%B8%D0%BD%D0%B3%D0%BE%D0%B2%D0%B5%D1%80

        Впрочем, все это лирика.

        У вас в программе нет ни скрещивания как такового, ни, строго говоря, мутаций. Вместо скрещивания у вас усреднение признаков, вместо мутаций — случайные изменения признаков в пределах каких-то лимитов. То есть это — не генетический алгоритм, а нечто совсем другое. Зато у вас есть вещи, не относящиеся к генетике напрямую: беготня особей по полю — это всего лишь очень хитрый способ построения оценочной функции.


        1. mihaild
          12.08.2015 17:46

          Ок, определение из вики:

          Отличительной особенностью генетического алгоритма является акцент на использование оператора «скрещивания»

          Где тут говорится про представление?

          «Скрещивание на уровне генов» — в том смысле, что перемещаются как единое целое гены, а не нуклеотиды.

          (и вы, видимо, перепутали меня с автором статьи)


          1. berez
            12.08.2015 19:11

            (и вы, видимо, перепутали меня с автором статьи)

            Да, перепутал, прошу прощения.

            Где тут говорится про представление?

            Нигде. Какие, по-вашему, из этого следует сделать выводы?

            «Скрещивание на уровне генов» — в том смысле, что перемещаются как единое целое гены, а не нуклеотиды.

            Для компьютерной симуляции это малосущественно и в большинстве случаев такими тонкостями можно пренебречь.


            1. mihaild
              13.08.2015 16:27

              Где тут говорится про представление?

              Нигде.

              Из этого можно сделать вывод, что утверждение
              Суть генетических алгоритмов в том, что генетическая информация хранится в родителях в виде битовой строчки. При скрещивании и мутировании тоже используется эта битовая строчка, безо всякого знания о ее внутреннем устройстве.

              неверно.


              1. berez
                13.08.2015 16:47

                Ценю вашу тягу к расстановке точек над Ё. Открываем священную книгу Тору на странице 1432 в очередной раз википедию, смотрим в текст:

                Genetic algorithms use linear binary representations. The most standard one is an array of bits. Arrays of other types and structures can be used in essentially the same way. The main property that makes these genetic representations convenient is that their parts are easily aligned due to their fixed size. This facilitates simple crossover operation. Variable length representations were also explored in Genetic algorithms, but crossover implementation is more complex in this case.


                В общем случае физическое представление генома (битовые строчки, байты, текст и т.п.) для работы генетического алгоритма не важно. Тем не менее, именно с битовых строчек генетические алгоритмы начались (см. работы Джона Холларда, которые я сам, впрочем, не читал).


                1. mihaild
                  14.08.2015 20:29

                  Т.е. crossover проще всего делать на битовых строках одинаковой длины, но можно и иначе.

                  Почему принципиально скрещивание без знания внутреннего устройства?
                  (в реальных организмах внутреннее устройство в виде деления на гены «известно»)

                  И почему случайные изменения параметров — не мутации?


                  1. berez
                    15.08.2015 01:35
                    +1

                    Почему принципиально скрещивание без знания внутреннего устройства?

                    Потому что тогда у нас собственно генетический алгоритм полностью изолирован от конкретных данных. Для «доказательства эволюции» — самый правильный подход.

                    Впрочем, это не так уж и принципиально. Просто универсальнее.

                    (в реальных организмах внутреннее устройство в виде деления на гены «известно»)

                    С некоторой натяжкой да. ЕМНИП, в местах, где возможен кроссинговер, есть довольно широкие «буферные зоны», не содержащие генов.

                    В компьютерной симуляции важны основные принципы (скрещивание, мутации, отбор). Многие присущие реальным организмам вещи (такие, как кроссинговер с учетом границ генов) в компьютерной симуляции малосущественны, т. к. ведут к увеличению времени работы или объемов потребляемой памяти, но при этом мало сказываются на конечном результате. Скажем, кроссинговер «посреди живого гена» по сути своей мало отличается от мутации в этом гене (а если ген у родителей одинаков, то вообще никак не проявится). Но для учета границ генов их придется как-то обозначать (+расход памяти), или вычислять (+расход времени), или завязывать всю реализацию под конкретное представление генов (потеря универсальности).

                    И почему случайные изменения параметров — не мутации?

                    В нашем конкретном случае на то есть несколько причин:
                    1. Это не совсем случайная мутация, а постепенное «гуляние» параметров. В реальном генетическом алгоритме одной случайной мутацией можно кардинально изменить ген, а в некоторых реализациях — вообще чуть ли не весь генотип сразу. Например, пропуск бита при копировании приведет к тому, что все гены, расположенные после мутации, изменятся. Здесь же все изменения постепенные.
                    2. Вероятность мутирования достаточно низка (в генетических алгоритмах задается отдельным параметром). Вероятность получить нежизнеспособный организм (в симуляции — неконкурентный) в результате мутации — весьма высока. Но можно получить и организм кардинально более «приспособленный». Смысл введения мутаций как раз в том, чтобы дать популяции возможность выйти из локального минимума (т. е. не дать популяции выродиться раньше времени). Здесь же «мутации», по сути, изменяют организм достаточно плавно, но при этом по всем параметрам сразу, поэтому нет смысла от них ждать каких-то «эволюционных скачков».

                    Ну и общее замечание по реализации: в генетическом алгоритме у потомка будет набор родительских генов (минус мутации, которые редкость). Здесь же у потомка весь генотип, по всем генам, всегда отличается от генотипа обоих родителей. То есть по сути — ничего общего с генетическими алгоритмами предложенная нам программа не имеет. Получился своеобразный поиск экстремума сложной функции методом случайных шагов.


  1. zogby
    10.08.2015 21:42

    интересно, но почему хищники и нехищники — это 2 разных класса. пусть они наследуют 1 класс, а способности вроде поедать, догонять, убегать реализовывались бы интерфейсами, учитывая массу, дальность зрения и наличие зубов, к примеру, мне кажется, так ближе к реальности. и тогда было бы интересно посмотреть, кто выживет — сильный хищник или быстрый нехищник)


    1. wrewolf
      10.08.2015 22:56

      А было бы интеренсо написать эдакий манйрафт для роботов. Где сервер раз в тик опрашивал бы «животных» по http json для унификации, а добаление новых животных бы свелось к живому добавлению параметров клиента животного в конфиг в сервер «мира»


      1. Ramdeif
        24.08.2015 11:19

        Вот здесь запахло чем-то интересным, но я не совсем понимаю, что вы имеете в виду. Вы предлагаете сделать что-то вроде онлайн игры? Напишите пожалуйста подробнее.


        1. wrewolf
          24.08.2015 16:38

          Именно онлайн игра. Скрещивание будет в пределах близких видов ( если авторы предусмотрят взаимодействие ботов), в остальных случаях в пределах вида. И так можно устраивать гик гонку ботов. Эдикий Spore, но для программ.


    1. Ramdeif
      24.08.2015 11:16

      Было это все давно, и на момент написания кода я еще очень плохо разбирался в наследовании, хотя то, что вы предлагаете было бы лучше. Спасибо.