
分形(Fractal)是具有自相似性的几何图形,小尺度下的形态与整体形态高度相似,典型代表有曼德博集合(Mandelbrot Set)、朱利亚集合(Julia Set)、科赫雪花、分形树等。Python结合matplotlib、numpy(高效数值计算)和numba(加速循环)可以轻松绘制出视觉效果惊艳的分形图,本文从基础原理到代码实战,教你画出高质量的分形图形。
一、前置准备
1.1 安装依赖
需要的核心库:
numpy:数值计算(矩阵运算、复数处理)matplotlib:绘图与色彩渲染numba:JIT编译加速(分形计算涉及大量循环,纯Python速度慢)PIL(可选):保存高清图片
执行安装命令:
bash
pip install numpy matplotlib numba pillow
1.2 核心优化思路
分形计算的核心是迭代判断点是否属于分形集合,循环次数多且计算密集:
- 用
numpy向量化运算替代纯Python循环; - 用
numba.jit装饰器编译核心函数,提速10~100倍; - 合理设置色彩映射(colormap)提升视觉效果。
二、实战1:曼德博集合(Mandelbrot Set)
曼德博集合是最经典的分形,定义为复平面上满足 ( z_{n+1} = z_n^2 + c )(初始 ( z_0=0 ))迭代不发散的点 ( c=x+yi ) 的集合。
2.1 完整代码(高清+彩色)
python
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from matplotlib.colors import LinearSegmentedColormap
# ===================== 1. 核心计算函数(Numba加速) =====================
@jit(nopython=True) # JIT编译,大幅提速
def mandelbrot(c, max_iter):
"""判断单个点c是否属于曼德博集合,返回迭代次数(用于上色)"""
z = 0
n = 0
while abs(z) <= 2 and n < max_iter:
z = z * z + c
n += 1
# 平滑上色:避免迭代次数突变导致的色块
if n == max_iter:
return max_iter # 属于集合,设为最大迭代次数
return n + 1 - np.log(np.log2(abs(z))) # 平滑后的迭代次数
@jit(nopython=True)
def mandelbrot_set(x_min, x_max, y_min, y_max, width, height, max_iter):
"""生成曼德博集合的迭代次数矩阵"""
x = np.linspace(x_min, x_max, width)
y = np.linspace(y_min, y_max, height)
img = np.zeros((height, width))
for i in range(height):
for j in range(width):
c = complex(x[j], y[i])
img[i, j] = mandelbrot(c, max_iter)
return img
# ===================== 2. 自定义色彩映射(更美观) =====================
def create_fractal_cmap():
"""创建自定义分形配色(深蓝→紫色→粉色→黄色→白色)"""
colors = [
(0.0, 0.0, 0.1), # 深蓝(集合内部)
(0.2, 0.0, 0.5), # 深紫
(0.5, 0.1, 0.8), # 粉紫
(0.8, 0.2, 1.0), # 亮粉
(1.0, 0.8, 0.2), # 黄色
(1.0, 1.0, 1.0) # 白色(迭代次数最多)
]
return LinearSegmentedColormap.from_list("fractal", colors, N=1024)
# ===================== 3. 绘制曼德博集合 =====================
def plot_mandelbrot():
# 1. 配置参数(可调整视角,比如放大局部细节)
# 全局视图:x∈[-2.0, 1.0], y∈[-1.5, 1.5]
# 局部细节(比如"海马谷"):x∈[-0.8, -0.7], y∈[0.0, 0.1]
x_min, x_max = -2.0, 1.0
y_min, y_max = -1.5, 1.5
width, height = 2000, 2000 # 分辨率(越高越清晰,计算越久)
max_iter = 1000 # 迭代次数(越高细节越多)
# 2. 计算分形数据
print("开始计算曼德博集合...")
img = mandelbrot_set(x_min, x_max, y_min, y_max, width, height, max_iter)
print("计算完成!")
# 3. 绘图配置
plt.figure(figsize=(10, 10), dpi=200) # dpi越高,图片越清晰
cmap = create_fractal_cmap() # 自定义配色
# 绘制(用log缩放让色彩过渡更自然)
plt.imshow(img, cmap=cmap, extent=(x_min, x_max, y_min, y_max), aspect="equal")
# 4. 美化设置(无坐标轴、标题等)
plt.axis("off") # 隐藏坐标轴
plt.tight_layout(pad=0) # 去除边距
plt.title("Mandelbrot Set", fontsize=16, color="white", pad=10) # 可选标题
# 5. 保存高清图片
plt.savefig(
"mandelbrot_set.png",
dpi=300, # 保存分辨率
bbox_inches="tight", # 去除白边
facecolor="black" # 背景色(分形背景用黑色更美观)
)
plt.show()
if __name__ == "__main__":
plot_mandelbrot()
2.2 关键优化与美化说明
- Numba加速 :
@jit(nopython=True)编译核心迭代函数,2000×2000分辨率的计算时间从几十分钟缩短到几十秒; - 平滑上色 :传统的"迭代次数上色"会出现明显色块,通过
n + 1 - np.log(np.log2(abs(z)))让色彩过渡更自然; - 自定义配色:避开matplotlib默认配色,用深蓝→紫色→黄色的渐变,贴合曼德博集合的视觉特征;
- 高清输出 :设置
dpi=300保存,无坐标轴、无白边,符合壁纸级视觉效果。
2.3 效果调整技巧
- 放大局部细节 :修改
x_min/x_max/y_min/y_max,比如聚焦"海马谷"(x∈[-0.8, -0.7], y∈[0.0, 0.1]),能看到更精细的分形结构; - 调整迭代次数 :
max_iter越大,细节越多(但计算越久),局部放大时建议设为2000+; - 更换配色 :修改
create_fractal_cmap中的颜色值,比如换成"青→绿→橙"的暖色调。
三、实战2:朱利亚集合(Julia Set)
朱利亚集合与曼德博集合同源,区别是迭代公式中 ( c ) 为固定常数,( z_0 ) 为复平面上的点(( z_{n+1}=z_n^2 + c ))。不同的 ( c ) 会生成完全不同的分形形态,视觉效果同样惊艳。
3.1 完整代码
python
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from matplotlib.colors import LinearSegmentedColormap
# ===================== 1. 核心计算函数 =====================
@jit(nopython=True)
def julia(z, c, max_iter):
"""判断点z是否属于朱利亚集合"""
n = 0
while abs(z) <= 2 and n < max_iter:
z = z * z + c
n += 1
if n == max_iter:
return max_iter
return n + 1 - np.log(np.log2(abs(z)))
@jit(nopython=True)
def julia_set(c, x_min, x_max, y_min, y_max, width, height, max_iter):
"""生成朱利亚集合数据"""
x = np.linspace(x_min, x_max, width)
y = np.linspace(y_min, y_max, height)
img = np.zeros((height, width))
for i in range(height):
for j in range(width):
z = complex(x[j], y[i])
img[i, j] = julia(z, c, max_iter)
return img
# ===================== 2. 自定义配色(冷色调) =====================
def create_julia_cmap():
colors = [
(0.0, 0.1, 0.2), # 深蓝黑
(0.1, 0.3, 0.8), # 蓝
(0.2, 0.8, 1.0), # 青
(0.5, 1.0, 0.8), # 浅青
(1.0, 1.0, 1.0) # 白
]
return LinearSegmentedColormap.from_list("julia", colors, N=1024)
# ===================== 3. 绘制朱利亚集合 =====================
def plot_julia():
# 1. 核心参数(不同的c对应不同形态,推荐几个经典值)
# c = -0.8 + 0.156j (经典螺旋)
# c = 0.285 + 0.01j (羽毛状)
# c = -0.7269 + 0.1889j (蝴蝶状)
c = complex(-0.8, 0.156)
x_min, x_max = -1.5, 1.5
y_min, y_max = -1.5, 1.5
width, height = 2000, 2000
max_iter = 1000
# 2. 计算数据
print("开始计算朱利亚集合...")
img = julia_set(c, x_min, x_max, y_min, y_max, width, height, max_iter)
print("计算完成!")
# 3. 绘图
plt.figure(figsize=(10, 10), dpi=200)
cmap = create_julia_cmap()
plt.imshow(img, cmap=cmap, extent=(x_min, x_max, y_min, y_max), aspect="equal")
plt.axis("off")
plt.tight_layout(pad=0)
plt.savefig(
"julia_set.png",
dpi=300,
bbox_inches="tight",
facecolor="black"
)
plt.show()
if __name__ == "__main__":
plot_julia()
3.2 经典c值推荐
不同的复数 ( c ) 会生成完全不同的朱利亚集合:
c = -0.8 + 0.156j:螺旋状结构,视觉冲击力强;c = 0.285 + 0.01j:羽毛状分形,细节丰富;c = -0.7269 + 0.1889j:蝴蝶状分形,对称美感;c = 0.45 + 0.1428j:类似星系的结构。
四、实战3:分形树(递归实现)
分形树是递归分形的经典案例,通过"主干→分支→子分支"的自相似递归生成,代码更简单,适合入门。
4.1 完整代码
python
import matplotlib.pyplot as plt
import numpy as np
# ===================== 1. 递归绘制分形树 =====================
def draw_fractal_tree(x, y, angle, length, depth, ax):
"""
递归绘制分形树
:param x/y: 当前起点坐标
:param angle: 当前分支角度(弧度)
:param length: 当前分支长度
:param depth: 递归深度
:param ax: 绘图轴
"""
if depth == 0:
return
# 计算分支终点坐标
dx = length * np.cos(angle)
dy = length * np.sin(angle)
x2 = x + dx
y2 = y + dy
# 绘制当前分支(深度越浅,颜色越绿,线条越粗)
color = (0.1, 0.6 + 0.3*(depth/10), 0.1) # 从深绿到浅绿
ax.plot([x, x2], [y, y2], color=color, linewidth=depth/2, solid_capstyle="round")
# 递归绘制左分支(角度偏转30°,长度缩短)
draw_fractal_tree(x2, y2, angle + np.pi/6, length * 0.7, depth - 1, ax)
# 递归绘制右分支(角度偏转30°,长度缩短)
draw_fractal_tree(x2, y2, angle - np.pi/6, length * 0.7, depth - 1, ax)
# ===================== 2. 绘制分形树 =====================
def plot_fractal_tree():
# 1. 初始化画布
fig, ax = plt.subplots(figsize=(10, 12), dpi=200)
ax.set_aspect("equal")
ax.set_xlim(-20, 20)
ax.set_ylim(0, 30)
ax.axis("off") # 隐藏坐标轴
ax.set_facecolor("#f0f0f0") # 浅灰色背景
# 2. 绘制分形树(起点在底部中间,初始角度向上,递归深度10)
draw_fractal_tree(0, 0, np.pi/2, 10, 10, ax)
# 3. 保存图片
plt.tight_layout(pad=0)
plt.savefig(
"fractal_tree.png",
dpi=300,
bbox_inches="tight",
facecolor="#f0f0f0"
)
plt.show()
if __name__ == "__main__":
plot_fractal_tree()
4.2 效果调整
- 递归深度 :
depth越大,树枝越多(建议10~12,太大易卡顿); - 分支角度 :修改
np.pi/6(30°),角度越大,树越"蓬松"; - 长度缩放 :修改
0.7,值越小,分支越短,树越紧凑; - 颜色 :调整
color的RGB值,比如换成"红→橙"的秋色调。
五、进阶美化技巧
5.1 色彩优化
- 避免纯黑/纯白:用深灰(
#101010)或浅灰(#f8f8f8)作为背景,更柔和; - 渐变配色:用
LinearSegmentedColormap自定义渐变,贴合分形的层次; - 对数缩放:绘图时用
np.log(img + 1)让低迭代次数的色彩过渡更自然。
5.2 高清输出
- 设置
dpi=300(打印级分辨率),bbox_inches="tight"去除白边; - 保存为PNG(无损)或SVG(矢量图,无限放大无锯齿);
- 对于超大分辨率(4000×4000+),可分块计算避免内存溢出。
5.3 动态效果(可选)
结合matplotlib.animation制作分形演化动画(比如曼德博集合放大过程):
python
# 示例:简单动画框架(需结合曼德博代码)
import matplotlib.animation as animation
fig, ax = plt.subplots(figsize=(10,10))
def update(frame):
# 每次帧调整x/y范围(放大局部)
x_min = -2.0 + frame*0.01
x_max = 1.0 - frame*0.01
img = mandelbrot_set(x_min, x_max, y_min, y_max, width, height, max_iter)
ax.imshow(img, cmap=cmap, extent=(x_min, x_max, y_min, y_max))
ax.axis("off")
return [ax]
ani = animation.FuncAnimation(fig, update, frames=100, interval=50)
ani.save("mandelbrot_animation.mp4", writer="ffmpeg", dpi=150)
六、总结
- 核心工具 :
numpy(数值)+matplotlib(绘图)+numba(加速)是绘制分形的黄金组合; - 美化关键:自定义配色、平滑上色、高清无白边输出,避开默认样式;
- 分形类型 :
- 曼德博/朱利亚集合:适合复杂精细的视觉效果,需Numba加速;
- 分形树:递归实现,简单易上手,适合入门;
- 扩展方向:可尝试科赫雪花、谢尔宾斯基三角形,或结合OpenCV添加滤镜效果。
通过以上代码和技巧,你可以轻松画出壁纸级的分形图,无论是用于学习、可视化还是艺术创作,都能达到专业级效果。