Генетический алгоритм. Часть 3 - Класс Population
Запись от ZackMercury размещена 19.06.2017 в 15:40
Переменные поколения.
В нашем случае, когда наша особь существует во времени, нужно ограничить особь временными рамками.
Сейчас я считаю, что 10 секунд будет достаточно для того, чтобы особь могла добраться до цели. Если же поставить больше, чем особи нужно, чтобы добраться до цели, алгоритм будет совершать лишние действия, чтобы потратить лишнее время, либо обходить не по прямой, а по дуге.
Также будем считать номер поколения для отчётности.
Количество популяции
Будем выводить минимальное расстояние при окончании жизни популяции в текстовое поле.
Наконец, сам массив особей:
Механика.
Нам необходимо знать время жизни текущей популяции, чтобы знать, когда нам нужно остановить её жизнь и отбирать родителей для новой популяции.
Нам нужно также знать целевую точку, к которой мы будем приближаться.
Нам также можно посчитать сумму весов, чтобы не считать её дважды при селекции каждой особи.
Ну и где-то нам нужно хранить препятствия, поэтому:
Основные методы класса Population
Начнём мы, конечно же, с конструктора.
Теперь разберём код обработчика добавления на сцену
private function init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); rects.push(new Rectangle(200, 300, 150, 20)); //добавляем 2 препятствия rects.push(new Rectangle(200, 100, 250, 20)); for (var i:int = 0; i < populationCount; i ++) { population.push(new Entity()); //создаём populationCount новых особей population[i].x = Main.W / 2; population[i].y = Main.H - 100; population[i].mutate(); //разнообразим наше население addChild(population[i]); } graphics.beginFill(0xFF2222); graphics.drawCircle(target.x, target.y, 3); //рисуем целевую точку for (i = 0; i < rects.length; i ++) graphics.drawRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height); //рисуем препятствия graphics.endFill(); }
public function update(deltaS:Number):void { if (deltaS > 0.1) //небольшой трюк для обработки ускорения и лагов, а также выход из неактивного состояния { var numEnters:int = deltaS / 0.1; //считаем кол-во частей по 100 мс в разнице между кадрами for (var i:int = 0; i < numEnters; i ++) update(0.1); //обновляем столько раз с частями по 100 мс update(deltaS - 0.1 * numEnters); //обновляем с остатком мс return; //выходим из функции } for (var i:int = 0; i < population.length; i ++) { population[i].update(deltaS); //обновляем население for (var j:int = 0; j < rects.length; j ++) //удаляем тех, кто столкнулся с препятствиями if (rects[j].contains(population[i].x, population[i].y)) { removeChild(population[i]); population.splice(i, 1); break; } } lifetime += deltaS; if (lifetime > generationLifetime) //если жизнь поколения больше установленной нами { lifetime = 0; //сброс calcFitness(); //считаем для каждой особи соответствие (fitness) totalWeights = 0; //считаем сумму всех весов for each(var e:Entity in population) { totalWeights += e.fitness; removeChild(e); } var e1:Entity = pickOne(); //отбираем родителей нового поколения var e2:Entity = pickOne(); population = new Vector.<Entity>(); for (var i:int = 0; i < populationCount; i ++) { var child:Entity = e1.clone(); //создаём новую особь с днк особи 1 child.crossbreed(e2); //скрещиваем её с особью 2 child.mutate(); //вызываем случайные мутации addChild(child); child.x = stage.stageWidth / 2; child.y = stage.stageHeight - 100; population.push(child); } generation ++; } }
package com.zackmercury.test { import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; import flash.geom.Rectangle; /** * ... * @author ZackMercury */ public class Population extends Sprite { public var populationCount:int = 200; private var population:Vector.<Entity> = new Vector.<Entity>(); public var generation:int = 0; private var generationLifetime:int = 10; public var lifetime:Number = 0; public var minDistance:Number = Number.MAX_VALUE; private var target:Point = new Point(400, 50); private var totalWeights:Number; private var rects:Vector.<Rectangle> = new Vector.<Rectangle>(); public function Population() { super(); addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); rects.push(new Rectangle(200, 300, 150, 20)); rects.push(new Rectangle(200, 100, 250, 20)); for (var i:int = 0; i < populationCount; i ++) { population.push(new Entity()); population[i].x = 640 / 2; population[i].y = 480 - 100; population[i].mutate(); addChild(population[i]); } graphics.beginFill(0xFF2222); graphics.drawCircle(target.x, target.y, 3); for (i = 0; i < rects.length; i ++) graphics.drawRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height); graphics.endFill(); } private function calcFitness():void { for (var i:int = 0; i < population.length; i ++) { var pos:Point = new Point(population[i].x, population[i].y); if (Point.distance(pos, target) < minDistance) minDistance = Point.distance(pos, target); population[i].fitness = Math.pow(1 / Point.distance(pos, target), 8); } } private function pickOne():Entity { var i:int = 0; var r:Number = Math.random(); while (r > 0) r -= population[i++].fitness / totalWeights; i--; return population[i]; } public function update(deltaS:Number):void { if (deltaS > 0.1) { var numEnters:int = deltaS / 0.1; for (var i:int = 0; i < numEnters; i ++) update(0.1); update(deltaS - 0.1 * numEnters); return; } for (var i:int = 0; i < population.length; i ++) { population[i].update(deltaS); for (var j:int = 0; j < rects.length; j ++) if (rects[j].contains(population[i].x, population[i].y)) { removeChild(population[i]); population.splice(i, 1); break; } } lifetime += deltaS; if (lifetime > generationLifetime) { lifetime = 0; calcFitness(); totalWeights = 0; for each(var e:Entity in population) { totalWeights += e.fitness; removeChild(e); } var e1:Entity = pickOne(); var e2:Entity = pickOne(); population = new Vector.<Entity>(); for (var i:int = 0; i < populationCount; i ++) { var child:Entity = e1.clone(); child.crossbreed(e2); child.mutate(); addChild(child); child.x = stage.stageWidth / 2; child.y = stage.stageHeight - 100; population.push(child); } generation ++; } } } }
package com.zackmercury.test { import com.bit101.components.InputText; import com.bit101.components.Label; import com.bit101.components.PushButton; import com.bit101.components.Slider; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.utils.getTimer; /** * ... * @author ZackMercury */ public class Main extends Sprite { public static const W:int = 640, H:int = 480; private var lastTick:int = 0; private var population:Population; private var lbl:Label; private var speed:Number = 1; public function Main() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point addEventListener(Event.ENTER_FRAME, update); population = new Population(); addChild(population); lbl = new Label(this, 0, 0, ""); var edit1:InputText = new InputText(this, 10, 45, population.populationCount.toString(), function(e:Event) { population.populationCount = (int(edit1.text)>0)?int(edit1.text):3; }); var speedSlider:Slider = new Slider(Slider.HORIZONTAL, this, 10, 65, function(e:Event):void { speed = speedSlider.value; }); speedSlider.maximum = 50; speedSlider.minimum = 1; speedSlider.value = 1; } private function update(e:Event = null):void { var currTick:int = getTimer(); var deltaS:Number = (currTick - lastTick) / 1000; population.update(deltaS * speed); lastTick = currTick; lbl.text = "Min distance:" + population.minDistance.toString() + "\n" + "Generation:" + population.generation + "\n" + "Lifetime:" + Math.round(population.lifetime*100)/100; } } }
MinimalComps_0_9_10.rar
Содержание
Всего комментариев 41
Комментарии
![]() ![]() |
|
Кто-то должен это сказать: без демок все это превращается в какой-то скучный ноучный труд. Это же блог. Даешь по swf-ке на каждый раздел!
|
![]() ![]() |
|
Zebestov, спасибо, в будущем постараюсь исправиться.
|
![]() ![]() |
|
Что ты хочешь получить от этого алгоритма в итоге?
|
![]() ![]() |
|
Ну, конкретно от этого я уже получил всё, что хотел
![]() |
![]() ![]() |
|
А, то есть это просто для автоматическкого обучения нейронной сети. Я просто особо не углублялся в эту тему)
А какую нейронку хочешь использовать? Гугловскую? |
![]() ![]() |
|
Нет конечно, я буду сам писать код для сети.
|
![]() ![]() |
|
Если есть готовые, зачем изобретать велосипед?
|
![]() ![]() |
|
С такой логикой и генетический алгоритм ни к чему, в DeepMind ведь используется Back propagation/Gradient Descent вместо эволюционных алгоритмов и ещё куча всего, уже давно написаного.
Или ты хочешь, чтобы я ничего не публиковал на эту тему? Ведь всё уже написано. |
|
Обновил(-а) ZackMercury 22.06.2017 в 11:49
|
![]() ![]() |
|
ZackMercury, не слушай их! Они тебе просто завидуют! Пиши ещё да побольше!
|
![]() ![]() |
|
Psycho Tiger, псевдокод уже тысячу раз написан, а охватить я хочу и новичков as well.
Чем больше людей крутится вокруг темы, тем интересней результаты мы сможем получить на выходе. У новичков тоже могут появиться гениальные идеи ![]() И да, ML непростая тема, но стоит постараться. Но я добавлю псевдокод тоже, спасибо. |
|
Обновил(-а) ZackMercury 22.06.2017 в 14:56
|
![]() ![]() |
|
Цитата:
С такой логикой
|
![]() ![]() |
|
Да, на AS3.
|
![]() ![]() |
|
Мне кажется, тебе стоит что-то вроде питона взять для таких целей.
|
![]() ![]() |
|
Перейти в интерпретируемый язык вместо транслируемого? Зачем?
Даже если я немного помню основные аспекты Python, чем он лучше? Мне уж удобней на С++ писать, чем на Python. |
![]() ![]() |
|
Интересно, кому будет не лень ставить Python для загрузки и просмотра демок?
P.S. это не Node.js был, случайно? Насчёт браузерного сомнения есть. Хотя всякое может быть, всё же AVM писали индусы из Adobe. |
|
Обновил(-а) ZackMercury 24.06.2017 в 23:15
|
![]() ![]() |
|
Да, речь о браузерном JavaScript.
|
![]() ![]() |
|
Цитата:
Перейти в интерпретируемый язык вместо транслируемого?
И да, питон я не люблю, но он лучше AS3 примерно всем. |
![]() ![]() |
|
А я по си грущу. Возможно современные тенденции гибкости просто не для такого слоупока, как я )
|
![]() ![]() |
|
Гибкость то такое
![]() |
![]() ![]() |
|
ZackMercury - JS и так типизированный) Я ж тебе написал выше.
UPD: Языки бывают типизированные и не типизированные. C не типизированными языками "в быту" мало кто сталкивается. Типизированные, в свою очередь делятся на статические и динамические, явные и неявные, строгие и нестрогие. Судя по всему, типизированным ЯП ты называешь язык с явной статической типизацией, например C++ или C#. JS - же, является неявным динамически типизированным языком, как и Python, как и Ruby (с последним могу ошибаться, Тигра, думаю меня поправит). |
|
Обновил(-а) FlashRus 28.06.2017 в 14:55
|
Последние записи от ZackMercury
- Вывод формулы для бесконечного цикла. (11.01.2019)
- Как заменить цикл на формулу. (10.01.2019)
- Конечные и бесконечные суммы, Ч. 1 (08.01.2019)
- Как легко запомнить тригонометрические функции (07.01.2019)
- Движение по треугольнику, квадрату, пентагону, хексагону, ... (05.01.2019)