Алгоритмы

индекс
298,92

Векторизуем изображение генетическим алгоритмом

Итак, на выходных мы должны весело отдохнуть, а потому попробуем векторизовать изображение генетическим алгоритмом.
Векторизованный доктор Хаус

Задача


Сделать из полупрозрачных многоугольников изображение максимально похожее на оригинал.

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

Демонстрация


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

Итак создание доктора Хауса из ста десятиугольников в течение 20 часов:


Исходные данные


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

Функцией приспособления будет сумма погрешностей цветов всех пикселов. Погрешность для пикселя определяем как
R*R + G*G + B*B, где R — разность между красной составляющей пикселя исходной картинки и полученной, G и B — зеленой и синей. Чем меньше значение функции, тем меньше погрешность и тем лучшую картинку мы получили.

Код


Определим класс точки полигона:
    [Serializable]
    public class Point: ICloneable
    {
        public int X { get; set; }
        public int Y { get; set; }

        public void SetRandom()
        {
            X = Helper.Rand.Next(0, Helper.Width);
            Y = Helper.Rand.Next(0, Helper.Height);
        }

        public void Mutate(Workarea workarea)
        {
            if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveMaxChance)
            {
                SetRandom();
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveMiddleChance)
            {
                X = Math.Min(Math.Max(0, X + Helper.Rand.Next(-Helper.MiddleRange, Helper.MiddleRange + 1)), Helper.Width);
                Y = Math.Min(Math.Max(0, Y + Helper.Rand.Next(-Helper.MiddleRange, Helper.MiddleRange + 1)), Helper.Height);
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveNearChance)
            {
                X = Math.Min(Math.Max(0, X + Helper.Rand.Next(-Helper.NearRange, Helper.NearRange + 1)), Helper.Width);
                Y = Math.Min(Math.Max(0, Y + Helper.Rand.Next(-Helper.NearRange, Helper.NearRange + 1)), Helper.Height);
                workarea.IsChange = true;
            }
        }

        public object Clone()
        {
            return new Point { X = X, Y = Y };
        }
    }


* This source code was highlighted with Source Code Highlighter.

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

Далее идет класс кисти, отвечающей за цвет полигона:
    [Serializable]
    public class Brush: ICloneable
    {
        public int A { get; set; }
        public int R { get; set; }
        public int G { get; set; }
        public int B { get; set; }

        public void SetRandom()
        {
            A = Helper.Rand.Next(30, 60);
            R = Helper.Rand.Next(0, 256);
            G = Helper.Rand.Next(0, 256);
            B = Helper.Rand.Next(0, 256);
        }

        public void Mutate(Workarea workarea)
        {
            if (Helper.Rand.NextDouble() <= Helper.MutationBrushAChance)
            {
                A = Helper.Rand.Next(30, 60);
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationBrushRChance)
            {
                R = Helper.Rand.Next(0, 256);
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationBrushGChance)
            {
                G = Helper.Rand.Next(0, 256);
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationBrushBChance)
            {
                B = Helper.Rand.Next(0, 256);
                workarea.IsChange = true;
            }
        }

        public object Clone()
        {
            return new Brush
            {
                A = A,
                R = R,
                G = G,
                B = B
            };
        }
    }


* This source code was highlighted with Source Code Highlighter.

У кисти есть A, R, G, B составляющие цвета. SetRandom инициализирует их случайными значениями. В процедуре Mutate каждая из составляющих может случайно измениться согласно своей вероятности выпадения мутации.

Вот мы и подошли к классу полигона:
    [Serializable]
    public class Polygon: ICloneable
    {
        public List<Point> Points { get; set; }
        public Brush Brush { get; set; }

        public void SetRandom()
        {
            Points = new List<Point>();

            Point center = new Point();
            center.SetRandom();

            for (int i = 0; i < Helper.MinPointsPerPolygon; i++)
            {
                Point point = new Point();
                point.X = Math.Min(Math.Max(0, center.X + Helper.Rand.Next(-3, 4)), Helper.Width);
                point.Y = Math.Min(Math.Max(0, center.X + Helper.Rand.Next(-3, 4)), Helper.Height);
                Points.Add(point);
            }

            Brush = new Brush();
            Brush.SetRandom();
        }

        public void Mutate(Workarea workarea)
        {
            if (Helper.Rand.NextDouble() <= Helper.MutationPolygonAddPointChance)
            {
                AddPoint();
                workarea.IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationPolygonDelPointChance)
            {
                DelPoint();
                workarea.IsChange = true;
            }

            Brush.Mutate(workarea);
            Points.ForEach(p => p.Mutate(workarea));
        }

        private void AddPoint()
        {
            if (Points.Count < Helper.MaxPointsPerPolygon)
            {
                Point point = new Point();
                int index = Helper.Rand.Next(1, Points.Count - 1);
                Point p1 = Points[index - 1];
                Point p2 = Points[index];
                point.X = (p1.X + p2.X) / 2;
                point.Y = (p1.Y + p2.Y) / 2;
                Points.Insert(index, point);
            }
        }

        private void DelPoint()
        {
            if (Points.Count > Helper.MinPointsPerPolygon)
            {
                int index = Helper.Rand.Next(0, Points.Count);
                Points.RemoveAt(index);
            }
        }

        public void Draw(Graphics g)
        {
            using (SolidBrush brush = new SolidBrush(Color.FromArgb(Brush.A, Brush.R, Brush.G, Brush.B)))
            {
                System.Drawing.Point[] points = Points.Select(p => new System.Drawing.Point(p.X,p.Y)).ToArray();
                g.FillPolygon(brush, points);
            }
        }

        public object Clone()
        {
            Polygon newpolygon = new Polygon();
            newpolygon.Brush = Brush.Clone() as Brush;
            newpolygon.Points = new List<Point>();
            Points.ForEach(p => newpolygon.Points.Add(p.Clone() as Point));
            return newpolygon;
        }
    }


* This source code was highlighted with Source Code Highlighter.

Полигон включает в себя массив точек и кисть (цвет). Процедура SetRandom инициализирует кисть, а также строит минимальное количество точек вокруг случайно выбранной. Мутация полигона представляет собой мутацию кисти и каждой точки полигона, а также с некоторой вероятностью может произойти добавление или удаление какой-то точки. Процедура Draw отрисовывает наш полигон.

Ну и наконец заключительный основной класс представляет собой рабочую область, содержащую наши многоугольники:
    [Serializable]
    public class Workarea: ICloneable
    {
        public List<Polygon> Polygons { get; set; }

        [XmlIgnore]
        public bool IsChange { get; set; }

        public void SetRandom()
        {
            Polygons = new List<Polygon>();

            for (int i = 0; i < Helper.MinPolygons; i++)
            {
                AddPolygon();
            }

            IsChange = true;
        }

        public void Mutate()
        {
            if (Helper.Rand.NextDouble() <= Helper.MutationWorkareaAddPolygonChance)
            {
                AddPolygon();
                IsChange = true;
            }

            if (Helper.Rand.NextDouble() <= Helper.MutationWorkareaDelPolygonChance)
            {
                DelPolygon();
                IsChange = true;
            }

            Polygons.ForEach(p => p.Mutate(this));
        }

        private void AddPolygon()
        {
            if (Polygons.Count < Helper.MaxPolygons)
            {
                Polygon polygon = new Polygon();
                polygon.SetRandom();
                Polygons.Add(polygon);
            }
        }

        private void DelPolygon()
        {
            if (Polygons.Count > Helper.MinPolygons)
            {
                int index = Helper.Rand.Next(0, Polygons.Count);
                Polygons.RemoveAt(index);
            }
        }

        public double Fitness(Color[,] colors)
        {
            double fitness = 0;
            Bitmap img = Draw();
            FastBitmap fastimg = new FastBitmap(img);
            for (int i = 0; i < Helper.Width; i++)
            {
                for (int j = 0; j < Helper.Height; j++)
                {
                    Color c1 = fastimg.GetPixel(i, j);
                    Color c2 = colors[i, j];
                    int r = c1.R - c2.R;
                    int g = c1.G - c2.G;
                    int b = c1.B - c2.B;
                    fitness += r * r + g * g + b * b;
                }
            }
            fastimg.Release();
            img.Dispose();
            return fitness;
        }

        public Bitmap Draw()
        {
            Bitmap img = new Bitmap(Helper.Width, Helper.Height);
            using (Graphics g = Graphics.FromImage(img))
            {
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.Clear(Color.Black);

                Polygons.ForEach(p => p.Draw(g));
            }
            return img;
        }

        public object Clone()
        {
            Workarea newarea = new Workarea();
            newarea.Polygons = new List<Polygon>();
            Polygons.ForEach(p => newarea.Polygons.Add(p.Clone() as Polygon));
            return newarea;
        }
    }


* This source code was highlighted with Source Code Highlighter.

Истинность свойства IsChange показывает, что где-то произошла мутация. SetRandom инициализирует минимальное начальное число полигонов. Mutate запускает мутацию для каждого полигона, а также с заданной вероятностью может произойти добавление или удаление полигона. Функция Fitness вычисляет функцию приспособления, ей передается массив цветов оригинальной картинки. Ну и функция Draw возвращает нашу отрисованную картинку.

Сам алгоритм выглядит так:
        private void Start()
        {
            if (workarea == null)
            {
                workarea = new Workarea();
                workarea.SetRandom();
            }

            while (isRunning)
            {
                Workarea newarea;
                lock (workarea)
                {
                    newarea = workarea.Clone() as Workarea;
                }
                newarea.Mutate();

                if (newarea.IsChange)
                {
                    double newfitness = newarea.Fitness(sourceColors);

                    if (newfitness <= fitness)
                    {
                        lock (workarea)
                        {
                            workarea = newarea;
                        }
                        fitness = newfitness;
                    }
                }
            }
        }


* This source code was highlighted with Source Code Highlighter.

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

Еще у нас остался вспомогательный класс, где хранятся настройки:
    public static class Helper
    {
        public static readonly Random Rand = new Random();

        public static int Width = 0;
        public static int Height = 0;

        public static int MinPointsPerPolygon = 3;
        public static int MaxPointsPerPolygon = 10;

        public static int MinPolygons = 0;
        public static int MaxPolygons = 100;

        public static int NearRange = 3;
        public static int MiddleRange = 20;
        
        public static double MutationPointMoveMaxChance = 0.0007;
        public static double MutationPointMoveMiddleChance = 0.0007;
        public static double MutationPointMoveNearChance = 0.0007;
        
        public static double MutationBrushAChance = 0.0007;
        public static double MutationBrushRChance = 0.0007;
        public static double MutationBrushGChance = 0.0007;
        public static double MutationBrushBChance = 0.0007;
        
        public static double MutationPolygonAddPointChance = 0.0007;
        public static double MutationPolygonDelPointChance = 0.0007;
        
        public static double MutationWorkareaAddPolygonChance = 0.0014;
        public static double MutationWorkareaDelPolygonChance = 0.0007;
    }


* This source code was highlighted with Source Code Highlighter.

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

UPD: спасибо за карму, перенес в блог Алгоритмы.
_________
Текст подготовлен в ХабраРедакторе
+191
30 августа 2009, 11:26
68

комментарии (84)

–93
tolikblik #
Блин всё хорошо, но зачем доктор Хаус, а? Зачем?
+52
SkywalkerY #
странно, я думал он всем нравится)
–62
tolikblik #
Понимаете, любые вещи имеют свойство надоедать. Осбенно картинки, которыми можно увидеть на каждом углу, куда не глянь…
Впрочем, это мое личное мнение, посмотрим на реакцию Хабрасообщества!
–43
tolikblik #
Ясно, похоже он и в правду всем нравится…
Уже карму опустили ниже плинтуса.
Странно, неужели я один такой?
–46
Liter #
Нет, не один. Не обращайте внимания на минусующее стадо. И чтобы такого больше не повторилось, запомните:
1) ХАУС КРУТОЙ КТО НЕ СМОТРИТ ТОТ ЛОХ
2) МАК ОЧЕНЬ КРУТОЙ ВЫ ПРОСТО НЕ ПОНИМАЕТЕ
3) ЛИНУКС НАГИБАЕТ РАКОМ ОСТАЛЬНЫЕ ЖАЛКИЕ ОПЕРАЦИОННЫЕ СИСТЕМЫ И ТОЛЬКО ПОПРОБУЙТЕ ВОЗРАЗИТЬ
4) PYTHON НЕ ИМЕЕТ АЛЬТЕРНАТИВ
5) КАРМА НЕ НУЖНА
6) ??????
7) PROFIT!
+66
SkywalkerY #
в таком случае вам повезло, что я не выбрал картинку, где хаус сидит за маком и кодит на питоне под линуксом…
–20
Liter #
Да мне-то всё равно, я давно уже не обращаю внимания на всё это. Просто tolikblik так был удивлён, что пришлось ему рассказать, как тут на хабре обстоят дела.
0
sclv #
Дела так обстоят не только на Хабре. Иначе бы у нас была давно эта… которая инновационная. А не край непуганных идиотов которые с перепугу все заминусуют.
0
sclv #
Кстати ребята количество плюсов минусов уже переваливает за сотню с чем то на тридцать коментов, где худо — бедно, но надо прикладывать хоть какое-то умственные усилия.
Любопытная статистика.
+2
Ferroman #
Слишком много кофе?
0
Arseniy #
Не согласен по всем пунктам, за исключением пунктов под номером шесть и семь.
0
aleks_raiden #
да
+2
r3r #
Меняйте аватар ;)
0
AgentSmith #
Людям стыдно признаваться в том, что они смотрят мыльные сериалы, ведь это удел домохозяек.
+2
Discover #
Готов поспорить, что фотка Путина вызвала бы другую реакцию. Хотя его почаще Хауса показывают.
+2
tolikblik #
Но не с высунутым языком, согласитесь!
+20
Discover #
Соглашусь. Но, блин, обсуждать какую фотку лучше использовать для испытания алгоритма…
+6
AndreyDmitriev #
Ну просто выбор несколько неожиданный, да ещё и с высунутым языком… А так общественность уже много раз проходила через выбор тестовых картинок. Обычно фотка «Lena Söderberg» используется для всяких тестов: en.wikipedia.org/wiki/Lenna. Ну а для разнообразия можно было необрезанную фотку взять. Ссылка на неё есть вот здесь www.ee.cityu.edu.hk/~lmpo/lenna/Lenna97.html примерно посередине (на работе лучше не открывать ;) ).
0
AndreyDmitriev #
Ах, ссылки покоцались… Исправляюсь...:
Статья в Википедии
Ещё одна статья
0
b3atb0x #
блин чувак, ну не нравится — не смотри. не заставляют же, автору скажи спасибо что он вобще что-то сюда запостил. писец, да сколько можно успокаивать заморачивающихся нытиков, ёпть.
+9
ferrari #
>>Блин всё хорошо, но зачем доктор Хаус, а? Зачем?
все хорошо, а вот аватар у вас — херня. Еще б «Не лох» в квадратике написали…
0
tolikblik #
Это был социальный эксперимент, который, похоже, с треском провалился! :-)
0
weiss #
Поставьте вместо плюса минус, может даст обратный эффект.
А лучше не париться по поводу цифр рядом с комментарием, тогда жить проще становится. =)
НЛО прилетело и опубликовало эту надпись здесь
0
v_k #
«Принцип сперматозоида»
+2
Liksys #
Еретик!
0
Kukalyakin #
А вы его смотрели?
–2
Sketch_Turner #
Не обращай внимания. Тут очень много небыдла, драчащие на мак/хауса/нужно добавить.
+3
grep0 #
Эксперимент забавный, но к генетическим алгоритмам не имеет никакого отношения, больше похоже на simulated annealing. Интересно, кстати, станет ли сходимость быстрее, если сделать настоящий отжиг, т.е. добавить в оценочную функцию «тепловые флуктуации»
+1
SkywalkerY #
действительно очень похоже на данный алгоритм, спасибо за ссылку, не знал о нем. с вашего позволения пока что оставлю в названии генетические алгоритмы.
+7
DileSoft #
Понравился русский перевод: «Алгоритм имитации отжига» — ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B8%D0%BC%D0%B8%D1%82%D0%B0%D1%86%D0%B8%D0%B8_%D0%BE%D1%82%D0%B6%D0%B8%D0%B3%D0%B0
+1
sclv #
А тут как раз подошел бы генетичексий. Поскольку металл имеет наследственность, сохраняющуюся и модифицирующкюся при можестве переплавок. Поэтому металлоломы всегда добавляют в свежую плавку для образовния правильного наследования.
0
grep0 #
OMG что вы курили? Неужели структурированную воду?
+1
MUSIC #
а не слышали что металл устает?
+1
Ferroman #
Вы бы хотя бы поинтересовались что значит это словосочетание, а уже потом несли бред с умным видом.
Какая к лешему усталость после переплавки?
+1
sclv #
Усталость металла это немного из другой оперы. Это просто микротрещины.
0
sclv #
Наследственость металла обысловленна микрокристалами, то есть кристаллами микронного размера которые практически не плавятся из-за своей очень компактной и чистой структуры и при застывании служат центрами образования кристализации. То есть образуют необходимое мелкое зерно. К этому надо добавить, что данные кристаллы образуются в холодном металле — так называемая нормализация, но в течении долгого времени. Поэтому найденные в земле немецкие каски и ножи имеют очень хорошее качество не только благодаря Золингеру. Свежая плавка из руды такого наследования не имеет.

0
rPman #
можно было бы нехило сэкономить на времени, если начальное наполнение трехугольниками было не случайное. Ну, например, изначально все трехугольники — одинаковые, расположенные рядышком, матрицей. Потом куда надо разъедутся.
0
SkywalkerY #
на самом деле прорисовка основного контура происходит очень быстро, а вот к концу очень замедляется, за последние часов 6-7 рендера мало что поменялось, значение функции фитнесс упало где-то всего лишь на 6000000, это при том, что значение функции на момент останова было 20276613
+5
moadib #
Это случайно не вот этим навеяно?
rogeralsing.com/2008/12/07/genetic-programming-evolution-of-mona-lisa/
+1
SkywalkerY #
верно, не мог вспомнить, где видел вдохновивший пример
–2
iDemy #
Теперь можно организовать стартап вроде imobilko и в день выкладывать по новой фотке, чуть ближе похожей на оригинал :)
+1
snusmumrik #
Он вас вдохновил так, что вы его повторили с точностью до имен классов (по крайней мере core-классы). Это правильно, что вы написали об этом примере на Хабре, но не стоило было присваивать только себе честь изобретения. Разве сложно всего лишь поставить ссылку: по мотивам того-то.
–12
Glow #
Но зачем?
+5
Spo1ler #
Just for fun, написано же
+5
naething #
Интересно, но без кроссовера и популяции назвать это генетическим алгоритмом у меня не повернулся бы язык :)
0
valyard #
Забавно.
Конечно, это не есть генетический алгоритм, но все равно апплодисменты!
+2
valyard #
Отличная первая статья btw
0
easy #
По мне так очень сложные для понимания вещи, но очень интересно. Занимаюсь рекламой… навеяло много идей…
+1
AlexcYeCu #
Судя по минусу, ваши идеи ужаснули кого-то ещё до их озвучивания…
+6
ramovsky #
При таком подходе это брутфорс а не ГА. Нужно было сделсть популяцию и скрещивание, тогда и результаты был бы получше, и время вычисления меньше…
+2
EvilX #
Столько хаусов обязательно передерутся.
0
neos712 #
Фото в некотором роде схоже с Эйнштейном =)
+1
Kindet #
Мб далеко не в тему, но кто может подсказать че за музыка играет в видео?)
+3
SkywalkerY #
John 5 — 2004 Vertigo — Flatlines, Thin Lines
0
Kindet #
спасибо
0
r1e #
Vertigo — отличнейший альбом!
+3
Masterkey #
навсякий случай, напиши на каком железе результат получен. может какнить соберусь заняться ГА, а результаты несчем сравнить
+1
SkywalkerY #
работало на 1 ядре частотой 1.73 GHz
0
Alaunquirie #
А если переписать под СUDA? И запустить, скажем, на Nvidia GTX 260 с 216 потоковыми камнями?
0
SkywalkerY #
проверьте) я с удовольствием почитаю о результатах
0
Alaunquirie #
habrahabr.ru/blogs/hardware/49910/

Напишите код, я поставлю карточку и проверю) Сейчас стоит HD4870.
0
mace #
Там и без CUDA есть куда оптимизировать. Большинство времени тратится на I/O при сохранении промежуточных результатов на диск. К тому же мутации можно распаралелить на количество ядер на машине. К сожалению просто воткнуть цикл в Parallel.ForAll не получилось — алгоритм не рассчитан на многопоточность, а разбиратся у меня сейчас нету времени, но все же мне кажется, что если пару часов посидеть над ним, то можно добится того же результата за намного меньшее время.
0
Alaunquirie #
Ну это просто как вариант, на мой взгляд если уж и делать что-то с графикой — так пусть работает GPU, который на это расчитан, и сохраняться можно всетки в рамку, благо времена с 256-512 мегабайтами давно прошли, и компов меньше, чем с 1 гигом почти не видно.
+4
Sho #

В журнале Esquire страницы с авторами имеют такой же вид, правда я думаю они не тратят на это по 20 часов за картинку, но тем неменее, результат получается довольно интересным
0
SharkyFLY #
А вы уверены что там используется та же технология?
0
Sho #
Нет конечно, я уверен что там не та же технология :)
Просто результат похожий и написано для скептиков которые сразу кричат, что зачем это все нужно и применить такое негде.
+7
s2d #
Это совсем другой алгоритм. Тут используется delaunay raster
image
www.jonathanpuckey.com/projects/delaunay-raster/
0
Sho #
Ооо! А вот за это огромное спасибо!
0
Ctacus #
от вашего алгоритма у Хауса заплыл правый глаз =)
+1
SkywalkerY #
он плачет от счастья…
+10
zerobrain #
Вариант на Javascript поинтересней будет ;)
+3
Denisio #
Два замечания:
1) прогнал профайлером — около половины времени проводится в GetPixel, надо как-то оптимизировать ;)
2) неплохо бы задействовать Parallel Extensions от MS. А то на коредуо только одно ядро загружено…

Еще перед началом мутаций можно расставить полигоны с вершинами в точках максимального градиента. Как мне кажется это значительно ускорит процесс.
+2
qmax #
демонстрация шикарна.
но было бы гораздо интереснее еслиб туда вставить счётчик поколений/мутаций.
абстрактное «20 часов» мало о чём говорит.

/**
ну и пояснения можно было бы подетальнее — для тех, кто не в теме генетических алгоритмов.
*/
0
SkywalkerY #
спасибо, обязательно учту на будущее
0
Gumoza #
СкайВолкер, спасибо — очень понравилось. Пара вопрос по делу (не про фотку Д.Хауса):
1. На какаой машине заняло 20 часов?
2. Как влияет на время количество многоугольников и количество углов?
0
SkywalkerY #
2. чем больше многоугольников и углов, тем быстрее мы получим то же изображение, но до определенного порога, потом будет опять медленно
–3
isapioff #
а где-же комментарии в коде?
+1
alternativshik #
Сударь, вы больны! (в хорошем смысле слова) =)
+2
mt_ #
Итого, имеем Джпег в 16,5 кБ против
100*10*( 2*sizeof(int) ) = 8000 байт, что после архивации даст что-то около 1 кБ.
Очень неплохой результат.
+2
atamur #
Как уже было сказано выше это совсем не генетический алгоритм (ничего схожего с живой эволюцией). Такого рода алгоритмы называются метод Монте-Карло, просто случайный перебор вариантов в надежде что найдем неплохой результат.
0
handymade #
жду Хауса, алгоритм — зачот!

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.