В 1985 году я прочитал статью Александра К. Дьюдни, ведущего раздел занимательной науки журнала “Scientific Americanએ”, о множестве Мандельброта, написал программу его визуализации на цветном телевизоре, подключенном через модуль крейта CAMAC к машинке MERA-CAMAC-125/СМ4А. После чего мы с коллегами могли часами генерировать и рассматривать завораживающие картинки, записывая выдающиеся в файлы на память. После упомянутой публикации подобные множества стали необычайно популярны, например, множество Мандельброта использовал в качестве своей эмблемы фонд Соросаએ. Гораздо позже, лет через десять, когда меня поразил Парадокс береговой линииએ, я узнал красивое и непонятное словосочетание «голоморфная динамика».
Голоморфная динамикаએ — область математики, где живут множество Мандельбротаએ и множество Жюлиаએ, где кроме красивых картинок есть красивые теоремы, а что самое главное, до сих пор есть неразгаданные загадки. Впрочем, я не математик и в этой области у меня самостоятельных работ нет, что, однако, не помешает вспомнить прошлое и рассказать, как строить завораживающие картинки на популярном языке Python.
Описание алгоритма
Итак, план такой. Пусть c — некоторое комплексное число. Рассмотрим последовательность чисел z0, z1, z2, … , которая строится следующим образом:
z0 = 0, zk+1 = zk2 + c, k = 0, 1, 2,…
На каждом шаге мы берём предыдущее число, возводим в квадрат и прибавляем c. В зависимости от значения c, последовательность чисел {zk} может быть ограниченной или неограниченной. При некоторых значениях она стремительно улетает в бесконечность (конечно в пределах разрядной сетки), а при некоторых тормозится. Если последовательность ограничена, мы говорим, что c принадлежит множеству Мандельброта M.
Поскольку число c комплексное, у него есть вещественная и мнимая части. Каждое комплексное число задаётся точкой декартовой плоскости: по горизонтальной координате будем откладывать вещественную часть, а по вертикальной — мнимую. Таким образом, множество M является множеством на вещественной плоскости.
Для построения графического изображения множества Мандельброта можно использовать алгоритм, называемый escape-time. Суть его такова. Всё множество полностью расположено внутри круга радиуса 2 на плоскости. Поэтому будем считать, что если для точки c последовательность итераций функции fc = z2 + c с начальным значением z = 0 после некоторого большого их числа N (скажем, 100) не вышла за пределы этого круга, то точка принадлежит множеству и красится в черный цвет. Соответственно, если на каком-то этапе, меньшем N, элемент последовательности по модулю стал больше 2, то точка множеству не принадлежит и остается белой. Таким образом, можно получить черно-белое изображение множества, которое и было получено Мандельбротом. Вот с этого мы и начнём.
Чёрно-белое множество, то которое получил Мандельброт
import numpy as np
import matplotlib.pyplot as plt
# библиотеки
# инициализиация
pmin, pmax, qmin, qmax = -2.5, 1.5, -2, 2
# пусть c = p + iq и p меняется в диапазоне от pmin до pmax,
# а q меняется в диапазоне от qmin до qmax
ppoints, qpoints = 200, 200
# число точек по горизонтали и вертикали
max_iterations = 300
# максимальное количество итераций
infinity_border = 100
# если ушли на это расстояние, считаем, что ушли на бесконечность
image = np.zeros((ppoints, qpoints))
# image — это двумерный массив, в котором будет записана наша картинка
# по умолчанию он заполнен нулями
for ip, p in enumerate(np.linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(np.linspace(qmin, qmax, qpoints)):
c = p + 1j * q
# буквой j обозначается мнимая единица: чтобы Python понимал, что речь
# идёт о комплексном числе, а не о переменной j, мы пишем 1j
z = 0
for k in range(max_iterations):
z = z ** 2 + c
# Самая Главная Формула
if abs(z) > infinity_border:
# если z достаточно большое, считаем, что последовательость
# ушла на бесконечность
# или уйдёт
# можно доказать, что infinity_border можно взять равным 4
image[ip, iq] = 1
# находимся вне M: отметить точку как белую
break
plt.xticks([])
plt.yticks([])
# выключим метки на осях
plt.imshow(-image.T, cmap='Greys')
# транспонируем картинку, чтобы оси были направлены правильно
# перед image стоит знак минус, чтобы множество Мандельброта рисовалось
# чёрным цветом
plt.show()

Чтобы сделать его цветным, можно, например, каждую точку не из множества красить в цвет, соответствующий номеру итерации, на котором её последовательность вышла за пределы круга.
Цветное множество, то которое за Мандельброта получили другие
import numpy as np
import matplotlib.pyplot as plt
# библиотеки
# инициализиация
pmin, pmax, qmin, qmax = -2.5, 1.5, -2, 2
# пусть c = p + iq и p меняется в диапазоне от pmin до pmax,
# а q меняется в диапазоне от qmin до qmax
ppoints, qpoints = 200, 200
# число точек по горизонтали и вертикали
max_iterations = 300
# максимальное количество итераций
infinity_border = 10
# если ушли на это расстояние, считаем, что ушли на бесконечность
def mandelbrot(pmin, pmax, ppoints, qmin, qmax, qpoints,
max_iterations=200, infinity_border=10):
image = np.zeros((ppoints, qpoints))
p, q = np.mgrid[pmin:pmax:(ppoints*1j), qmin:qmax:(qpoints*1j)]
c = p + 1j*q
z = np.zeros_like(c)
for k in range(max_iterations):
z = z**2 + c
mask = (np.abs(z) > infinity_border) & (image == 0)
image[mask] = k
z[mask] = np.nan
return -image.T
image = mandelbrot(-2.5, 1.5, 1000, -2, 2, 1000)
plt.xticks([])
plt.yticks([])
plt.imshow(image, cmap='flag', interpolation='none')
plt.show()

Изменяем палитру множества Мандельброта
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
import matplotlib.colors as clr
# библиотеки
# инициализиация
pmin, pmax, qmin, qmax = -2.5, 1.5, -2, 2
# пусть c = p + iq и p меняется в диапазоне от pmin до pmax,
# а q меняется в диапазоне от qmin до qmax
ppoints, qpoints = 200, 200
# число точек по горизонтали и вертикали
max_iterations = 300
# максимальное количество итераций
infinity_border = 10
# если ушли на это расстояние, считаем, что ушли на бесконечность
def mandelbrot(pmin, pmax, ppoints, qmin, qmax, qpoints,
max_iterations=200, infinity_border=10):
image = np.zeros((ppoints, qpoints))
p, q = np.mgrid[pmin:pmax:(ppoints*1j), qmin:qmax:(qpoints*1j)]
c = p + 1j*q
z = np.zeros_like(c)
for k in range(max_iterations):
z = z**2 + c
mask = (np.abs(z) > infinity_border) & (image == 0)
image[mask] = k
z[mask] = np.nan
return -image.T
#image = mandelbrot(-0.793191078177363, 0.16093721735804, 1000, -0.793191, 0.160937, 1000)
plt.figure(figsize=(10, 10))
colorpoints = [(1 - (1 - q) ** 4, c) for q, c in zip(np.linspace(0, 1, 20),
cycle(['#ffff88', '#000000',
'#ffaa00', ]))]
cmap = clr.LinearSegmentedColormap.from_list('mycmap',
colorpoints, N=2048)
# LinearSegmentedColormap создаёт палитру по заданным точкам и заданным цветам
# можете попробовать выбрать другие цвета
plt.xticks([])
plt.yticks([])
image = mandelbrot(-2.5, 1.5, 1000, -2, 2, 1000)
plt.imshow(image, cmap=cmap, interpolation='none')
plt.show()

Углубляемся
А теперь в наш алгоритм добавим еще один цикл и рассмотрим поподробнее множество Мандельброта в окрености точки -0.793191078177363, 0.16093721735804
p_center, q_center = -0.793191078177363, 0.16093721735804
for i in range(1,11):
scalefactor = i / 12000
plt.xticks([])
plt.yticks([])
pmin_ = (pmin - p_center) * scalefactor + p_center
qmin_ = (qmin - q_center) * scalefactor + q_center
pmax_ = (pmax - p_center) * scalefactor + p_center
qmax_ = (qmax - q_center) * scalefactor + q_center
image = mandelbrot(pmin_, pmax_, 500, qmin_, qmax_, 500)
print("(", pmin_, ",", pmax_, ") (", qmin_, ",", qmax_, ")")
plt.figure(figsize=(10, 10))
plt.imshow(image, cmap='flag', interpolation='none')
filename = "images//mandelbrot-" + str(i) + ".png"
plt.savefig(filename, format="png")
print(filename + " saved")
Думаю, что комментировать здесь особо нечего, итак все понятно, просто наслаждайтесь чудесами из мира фракталов.
Посмотрели. Дизайнеры, кое-что понимающие в кодировании, могут использовать приведенные алгоритмы в своем творчестве для создания оригинальных картинок и фантастических сюжетом. Все они проверены в PyCharm Community.
В качестве основы этих алгоритмов использованы коды Ильи Щурова, которые значительно переработаны и дополнены.









