0,0
рейтинг
16 декабря 2012 в 20:49

Разработка → Построение фрактальных фигур в Matlab

«Итерация от человека. Рекурсия — от Бога.» Л. Питер Дойч

Введение


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

Про фракталы


В одной из передач цикла BBC (Тайная жизнь хаоса) позиционировалась интересная мысль, позиционировалась она конечно не авторами этого видео, а Аланом Тьюрингом и отцом теории хаоса Эдвардом Лоренцем. Как оказалось, сложные системы, с большим числом связей и элементов (пусть даже однообразных) имеют порог предсказуемости. Что это значит? Совокупность простейших структур с детерминированной логикой может на выходе давать весьма и весьма сложное поведение. Почти так и получается в нашем случае: взяв простое рекуррентное соотношение Z[i+1] = Z[i]^(n) + C, i = 1, 2,… inf где С — комплексное число, Z[0] = 0, увидим, что некоторые суммы будут конечны, а некоторые будут убегать в бесконечность (в зависимости от выбранной С). Ниже будет код, будет понятнее. Большой интерес вызывает поведение точек на границе расходимости. Они формируют сложные, порой самоповторяющиеся узоры, которые могут меняться при увеличении степени масштабирования, генерируя бесконечный динамический рисунок. Порой наблюдать за этими рисунками бывает интересно, изменяя степень полинома, или ставя в рекурсивную формулу новые функции, можно получить очень интересные картинки.

Написание кода


Начнем с написания скрипта fractal.m: Зададим размер картинки 500x500 пикселей, для нас будет интересна область [-2, 1]; по действительной оси и [-1.5, 1.5]; по мнимой оси, в ней мы и будем наблюдать фрактал. Если сумма ряда выходит за границы этого квадрата, то мы считаем, что ряд расходится.
image_size = 500;

bound_re = [-2, 1];
bound_im = [-1.5, 1.5];



Далее мы рисуем фрактал с помощью функции draw_fractal, её мы рассмотрим далее. Она принимает ограничивающий прямоугольник и размер картинки на вход. Данная функция возвращает пересчитанные размеры пикселя на увеличенном участке т.е. pb_re pb_im — математический размер пикселя по мнимой и действительной оси. Далее мы выбираем область для приближения через getrect , current_point, — левая верхняя точка прямоугольника зумирования, получаем ширину и высоту ограничивающего прямоугольника, заданного мышью. bound_re и bound_im новые границы рассматриваемой области (аналогично начальным). Далее все повторяется.

while(1)
    [pb_re pb_im] = draw_fractal(bound_re, bound_im, image_size);
    
    rect = getrect;
    current_point = complex(bound_re(1) + rect(1) * pb_re - 0.5 * pb_re , ...
        bound_im(1) + rect(2) * pb_im - 0.5 * pb_im);
    
    current_width = rect(3) * pb_re;
    current_height = rect(4) * pb_im;
    
    bound_re = [real(current_point), real(current_point) + current_width];
    bound_im = [imag(current_point), imag(current_point) + current_height];
    
end


Функция draw_fractal.m вычисляет какому математическому размеру соответствует пиксель pixel_bounds_re и pixel_bounds_im, далее мы идем по матрице изображения и рассматриваем математическое множество точек, лежащих в центре наших коробочек-пикселей, для каждой точки с помощью функции [color] = is_a_m_point(current_point) вычисляем убегает ли она в бесконечность или нет.

function [pixel_bounds_re, pixel_bounds_im]=draw_fractal( bound_re,  bound_im, image_size)

    pixel_bounds_re = (bound_re(2) - bound_re(1) ) / image_size;
    pixel_bounds_im = (bound_im(2) - bound_im(1) ) / image_size;
    
    frac = zeros([image_size, image_size]);
    
    parfor re = 1 : image_size
        for im = 1 : image_size
            current_point = complex(bound_re(1) + re * pixel_bounds_re - 0.5 * pixel_bounds_re , bound_im(1) + ...
            im * pixel_bounds_im - 0.5 * pixel_bounds_im);
        
            [color] = is_a_m_point(current_point);
            
            frac(im,re) = color;

        end
    end
    frac = mat2gray(frac);
    imshow(frac);
end


Причем чем быстрее точка будет убегать в бесконечность, тем светлее она будет, это видно из функции is_a_m_point.m Она принимает на вход нашу константу С из рекурсивной формулы Z[i + 1] = Z[i] ^ (n) + C, задача функции состоит в том, чтобы вернуть цвет color, — чем быстрее ряд расходится тем ярче цвет, если точка убегает за границы [-2 1] (реал ось) [-1.5 1.5] (мнимая ось), то мы считаем, что ряд расходится. Мы считаем, что Z[0] = 0, а в ряде суммируем 50 чисел (как правило этого достаточно, чтобы понять разойдется ряд или нет).

function [ color] = is_a_m_point( constant )
    color = 0;
    z = 0; % это Z[0]
    
    for i = 1 : 50
         z = z^(2) / (1 + z + z^(4)) + constant; % используемая рекурсивная зависимость

        if real(z) < -2 || real(z) > 1 || imag(z) > 1.5 || imag(z) < -1.5
                color = 255 - 5.5 * (i - 1);
                return;
        end
    end
end


Вот и всё. Написав пару скриптов можно расслабиться и попытаться исследовать, что получилось в результате. Так как бедное множество Мондельброта уже где только не светилось, попробуем исследовать что-нибудь поинтереснее, например функцию z = z^(2) / (1 + z + z^(4)) + constant;

Визуализация


Указанная зависимость порождает на комплексной плоскости следующий рисунок:
image
Левая центральная часть этой картины интересна тем, что приближая её мы будем падать в бесконечный обновляющийся паттерн самоподобных фигур. Прекрасная аллегория к тому, что многообразие может быть порождено группой конечных примитивов.
image
Используя представленные матлабовские скрипты легко получить и динамику изменения фрактальных узоров на плоскости, например меняя степень полинома n в рекурсивной формуле Z[i] = Z[i-1] ^ (n) + C, n = 1, 2… 200 можно получить следующее


Заключение


В данной статье я попытался представить простейшие вариант реализации скриптов в среде Matlab, создающих фрактальные узоры от произвольной рекурсивной зависимости с возможностью масштабирования. Легко заметить, что каждая зависимость рождает по-своему уникальный мир на комплексной плоскости, со своими «законами формирования узоров», со своей симметрией. Из всего этого можно сделать много интуитивных умозаключений, некоторые из которых я уже приводил в тексте статьи. В цикле своих лекций Франклин Меррелл-Вольф (Математика, философия йога) ставит вопрос о двойственности человеческого мышления и стремлении человека к дескретизации реальности, а так же ставит вопрос «А правда ли, что мир из чего-то состоит?». Так состоит ли он, или во главе всего стоит одна рекурсивная формула, которая и порождает необъятное многообразие действительности?
Александр Ященко @alexhoppus
карма
22,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

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

  • +1
      for i = 1 : 50
             z = z^(2) / (1 + z + z^(4)) + constant; % используемая рекурсивная зависимость
    
            if real(z) < -2 || real(z) > 1 || imag(z) > 1.5 || imag(z) < -1.5
                    color = 255 - 5.5 * (i - 1);
                    return;
            end
        end
    

    Не очень понятно каким образом выбраны константы [-2, 1] [-1.5; 1.5] и почему бы не использовать модуль комплексного числа для определения расходимости ряда. Другими словами, для каждой рекуррентной формулы, границы расходимости свои, и эти границы математически строго доказываются. Более того, на вашем рисунке видно, что существуют С вне этой границы такие, что последовательность на основе этого С не расходится.

    Константа 5.5 для цвета нарушает непрерывность. Для i = 50, color будет -14.5. Это специально?

    Не уверен, но похоже термин рекурсивной зависимости тут не уместен. Следует использовать «рекуррентная формула»
    • 0
      Вообще говоря константы [-2, 1] [-1.5; 1.5] были выбраны когда я строил множество Мондельброта, это просто начальная область в которой мы начинаем рассматривать фрактал. Можете другие выбрать. Про рекуррентную формулу — согласен.
      Про цвет — нет не специально, константа -14.5 для матлаба эквивалентна 0 (черному). Вообще я ее (константу 5.5) подбирал экспериментально из собственных эстетических воззрений.
      • +1
        Для множества Мандельброта математически доказано, что последовательность расходится, когда |z| > 2.
  • 0
    Если рассматривать произвольный случай может просто имеет смысл расширить границы расходимости и надеяться, что их неточное указание для произвольного случая не сильно исказит картину? (хотя итак со скоростью проблемы) Для множества Мандельброта тогда надо было рассматривать границы [-2, 2] [-2; 2], хотя картина вроде не сильно меняется по сравнению [-2, 1] [-1.5; 1.5]. Спасибо, что прояснили.
    • 0
      В случае наличия неуверенности, нужно выбирать границу крайне большую (например |z| > 10^5), это практически гарантирует, что в случае наличия границы — все будет отлично, и на скорость не сильно повлияет, поскольку расхождение обычно происходит крайне быстро.

      (|z| > 2 ) !<-> (z > [-2,2] [-2,2])
      • 0
        да, я понимаю, что не эквивалентно. спасибо большое, надо код функции определения принадлежности точки переписать след образом
        function [ color] = is_a_m_point( constant )
            color = 0;
            z = 0; 
            for i = 1 : 100
                  z = z^(2)  + constant;
                if abs(z) > 10^5
                        color = 255 - 2.55 * (i - 1);
                        return;
                end
            end
        end
        

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