Генетический алгоритм. Часть 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
Комментарии
20.06.2017 12:09 | |
Кто-то должен это сказать: без демок все это превращается в какой-то скучный ноучный труд. Это же блог. Даешь по swf-ке на каждый раздел!
|
20.06.2017 12:14 | |
Zebestov, спасибо, в будущем постараюсь исправиться.
|
21.06.2017 12:00 | |
Что ты хочешь получить от этого алгоритма в итоге?
|
21.06.2017 12:31 | |
Ну, конкретно от этого я уже получил всё, что хотел В итоге я получу необходимые навыки в последовательном путешествии к эволюции нейросетей.
|
22.06.2017 06:10 | |
А, то есть это просто для автоматическкого обучения нейронной сети. Я просто особо не углублялся в эту тему)
А какую нейронку хочешь использовать? Гугловскую? |
22.06.2017 10:14 | |
Нет конечно, я буду сам писать код для сети.
|
22.06.2017 10:37 | |
Если есть готовые, зачем изобретать велосипед?
|
22.06.2017 10:42 | |
С такой логикой и генетический алгоритм ни к чему, в DeepMind ведь используется Back propagation/Gradient Descent вместо эволюционных алгоритмов и ещё куча всего, уже давно написаного.
Или ты хочешь, чтобы я ничего не публиковал на эту тему? Ведь всё уже написано. |
|
Обновил(-а) ZackMercury 22.06.2017 в 11:49
|
22.06.2017 13:48 | |
ZackMercury, не слушай их! Они тебе просто завидуют! Пиши ещё да побольше!
|
22.06.2017 13:57 | |
Psycho Tiger, псевдокод уже тысячу раз написан, а охватить я хочу и новичков as well.
Чем больше людей крутится вокруг темы, тем интересней результаты мы сможем получить на выходе. У новичков тоже могут появиться гениальные идеи И да, ML непростая тема, но стоит постараться. Но я добавлю псевдокод тоже, спасибо. |
|
Обновил(-а) ZackMercury 22.06.2017 в 14:56
|
22.06.2017 19:43 | |
Цитата:
С такой логикой
|
23.06.2017 09:53 | |
Да, на AS3.
|
23.06.2017 13:56 | |
Мне кажется, тебе стоит что-то вроде питона взять для таких целей.
|
24.06.2017 12:35 | |
Перейти в интерпретируемый язык вместо транслируемого? Зачем?
Даже если я немного помню основные аспекты Python, чем он лучше? Мне уж удобней на С++ писать, чем на Python. |
24.06.2017 21:10 | |
Интересно, кому будет не лень ставить Python для загрузки и просмотра демок?
P.S. это не Node.js был, случайно? Насчёт браузерного сомнения есть. Хотя всякое может быть, всё же AVM писали индусы из Adobe. |
|
Обновил(-а) ZackMercury 24.06.2017 в 23:15
|
26.06.2017 12:46 | |
Да, речь о браузерном JavaScript.
|
26.06.2017 19:30 | |
Цитата:
Перейти в интерпретируемый язык вместо транслируемого?
И да, питон я не люблю, но он лучше AS3 примерно всем. |
28.06.2017 11:57 | |
А я по си грущу. Возможно современные тенденции гибкости просто не для такого слоупока, как я )
|
28.06.2017 12:27 | |
Гибкость то такое Типизированные языки гораздо легче читаются(не считая Pascal/Delphi) + гораздо быстрее выполняются
|
28.06.2017 14:26 | |
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)