使用python繪制好看的分形圖(附代碼)
分形(Fractal)是具有自相似性的幾何圖形,小尺度下的形態(tài)與整體形態(tài)高度相似,典型代表有曼德博集合(Mandelbrot Set)、朱利亞集合(Julia Set)、科赫雪花、分形樹等。Python結(jié)合matplotlib、numpy(高效數(shù)值計(jì)算)和numba(加速循環(huán))可以輕松繪制出視覺效果驚艷的分形圖,本文從基礎(chǔ)原理到代碼實(shí)戰(zhàn),教你畫出高質(zhì)量的分形圖形。
一、前置準(zhǔn)備
1.1 安裝依賴
需要的核心庫(kù):
numpy:數(shù)值計(jì)算(矩陣運(yùn)算、復(fù)數(shù)處理)matplotlib:繪圖與色彩渲染numba:JIT編譯加速(分形計(jì)算涉及大量循環(huán),純Python速度慢)PIL(可選):保存高清圖片
執(zhí)行安裝命令:
pip install numpy matplotlib numba pillow
1.2 核心優(yōu)化思路
分形計(jì)算的核心是迭代判斷點(diǎn)是否屬于分形集合,循環(huán)次數(shù)多且計(jì)算密集:
- 用
numpy向量化運(yùn)算替代純Python循環(huán); - 用
numba.jit裝飾器編譯核心函數(shù),提速10~100倍; - 合理設(shè)置色彩映射(colormap)提升視覺效果。
二、實(shí)戰(zhàn)1:曼德博集合(Mandelbrot Set)
曼德博集合是最經(jīng)典的分形,定義為復(fù)平面上滿足 ( z_{n+1} = z_n^2 + c )(初始 ( z_0=0 ))迭代不發(fā)散的點(diǎn) ( c=x+yi ) 的集合。
2.1 完整代碼(高清+彩色)
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from matplotlib.colors import LinearSegmentedColormap
# ===================== 1. 核心計(jì)算函數(shù)(Numba加速) =====================
@jit(nopython=True) # JIT編譯,大幅提速
def mandelbrot(c, max_iter):
"""判斷單個(gè)點(diǎn)c是否屬于曼德博集合,返回迭代次數(shù)(用于上色)"""
z = 0
n = 0
while abs(z) <= 2 and n < max_iter:
z = z * z + c
n += 1
# 平滑上色:避免迭代次數(shù)突變導(dǎo)致的色塊
if n == max_iter:
return max_iter # 屬于集合,設(shè)為最大迭代次數(shù)
return n + 1 - np.log(np.log2(abs(z))) # 平滑后的迭代次數(shù)
@jit(nopython=True)
def mandelbrot_set(x_min, x_max, y_min, y_max, width, height, max_iter):
"""生成曼德博集合的迭代次數(shù)矩陣"""
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():
"""創(chuàng)建自定義分形配色(深藍(lán)→紫色→粉色→黃色→白色)"""
colors = [
(0.0, 0.0, 0.1), # 深藍(lán)(集合內(nèi)部)
(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) # 白色(迭代次數(shù)最多)
]
return LinearSegmentedColormap.from_list("fractal", colors, N=1024)
# ===================== 3. 繪制曼德博集合 =====================
def plot_mandelbrot():
# 1. 配置參數(shù)(可調(diào)整視角,比如放大局部細(xì)節(jié))
# 全局視圖:x∈[-2.0, 1.0], y∈[-1.5, 1.5]
# 局部細(xì)節(jié)(比如“海馬谷”):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 # 分辨率(越高越清晰,計(jì)算越久)
max_iter = 1000 # 迭代次數(shù)(越高細(xì)節(jié)越多)
# 2. 計(jì)算分形數(shù)據(jù)
print("開始計(jì)算曼德博集合...")
img = mandelbrot_set(x_min, x_max, y_min, y_max, width, height, max_iter)
print("計(jì)算完成!")
# 3. 繪圖配置
plt.figure(figsize=(10, 10), dpi=200) # dpi越高,圖片越清晰
cmap = create_fractal_cmap() # 自定義配色
# 繪制(用log縮放讓色彩過(guò)渡更自然)
plt.imshow(img, cmap=cmap, extent=(x_min, x_max, y_min, y_max), aspect="equal")
# 4. 美化設(shè)置(無(wú)坐標(biāo)軸、標(biāo)題等)
plt.axis("off") # 隱藏坐標(biāo)軸
plt.tight_layout(pad=0) # 去除邊距
plt.title("Mandelbrot Set", fontsize=16, color="white", pad=10) # 可選標(biāo)題
# 5. 保存高清圖片
plt.savefig(
"mandelbrot_set.png",
dpi=300, # 保存分辨率
bbox_inches="tight", # 去除白邊
facecolor="black" # 背景色(分形背景用黑色更美觀)
)
plt.show()
if __name__ == "__main__":
plot_mandelbrot()
2.2 關(guān)鍵優(yōu)化與美化說(shuō)明
- Numba加速:
@jit(nopython=True)編譯核心迭代函數(shù),2000×2000分辨率的計(jì)算時(shí)間從幾十分鐘縮短到幾十秒; - 平滑上色:傳統(tǒng)的“迭代次數(shù)上色”會(huì)出現(xiàn)明顯色塊,通過(guò)
n + 1 - np.log(np.log2(abs(z)))讓色彩過(guò)渡更自然; - 自定義配色:避開matplotlib默認(rèn)配色,用深藍(lán)→紫色→黃色的漸變,貼合曼德博集合的視覺特征;
- 高清輸出:設(shè)置
dpi=300保存,無(wú)坐標(biāo)軸、無(wú)白邊,符合壁紙級(jí)視覺效果。
2.3 效果調(diào)整技巧
- 放大局部細(xì)節(jié):修改
x_min/x_max/y_min/y_max,比如聚焦“海馬谷”(x∈[-0.8, -0.7], y∈[0.0, 0.1]),能看到更精細(xì)的分形結(jié)構(gòu); - 調(diào)整迭代次數(shù):
max_iter越大,細(xì)節(jié)越多(但計(jì)算越久),局部放大時(shí)建議設(shè)為2000+; - 更換配色:修改
create_fractal_cmap中的顏色值,比如換成“青→綠→橙”的暖色調(diào)。
三、實(shí)戰(zhàn)2:朱利亞集合(Julia Set)
朱利亞集合與曼德博集合同源,區(qū)別是迭代公式中 ( c ) 為固定常數(shù),( z_0 ) 為復(fù)平面上的點(diǎn)(( z_{n+1}=z_n^2 + c ))。不同的 ( c ) 會(huì)生成完全不同的分形形態(tài),視覺效果同樣驚艷。
3.1 完整代碼
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from matplotlib.colors import LinearSegmentedColormap
# ===================== 1. 核心計(jì)算函數(shù) =====================
@jit(nopython=True)
def julia(z, c, max_iter):
"""判斷點(diǎn)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):
"""生成朱利亞集合數(shù)據(jù)"""
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. 自定義配色(冷色調(diào)) =====================
def create_julia_cmap():
colors = [
(0.0, 0.1, 0.2), # 深藍(lán)黑
(0.1, 0.3, 0.8), # 藍(lán)
(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. 核心參數(shù)(不同的c對(duì)應(yīng)不同形態(tài),推薦幾個(gè)經(jīng)典值)
# c = -0.8 + 0.156j (經(jīng)典螺旋)
# 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. 計(jì)算數(shù)據(jù)
print("開始計(jì)算朱利亞集合...")
img = julia_set(c, x_min, x_max, y_min, y_max, width, height, max_iter)
print("計(jì)算完成!")
# 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 經(jīng)典c值推薦
不同的復(fù)數(shù) ( c ) 會(huì)生成完全不同的朱利亞集合:
c = -0.8 + 0.156j:螺旋狀結(jié)構(gòu),視覺沖擊力強(qiáng);c = 0.285 + 0.01j:羽毛狀分形,細(xì)節(jié)豐富;c = -0.7269 + 0.1889j:蝴蝶狀分形,對(duì)稱美感;c = 0.45 + 0.1428j:類似星系的結(jié)構(gòu)。
四、實(shí)戰(zhàn)3:分形樹(遞歸實(shí)現(xiàn))
分形樹是遞歸分形的經(jīng)典案例,通過(guò)“主干→分支→子分支”的自相似遞歸生成,代碼更簡(jiǎn)單,適合入門。
4.1 完整代碼
import matplotlib.pyplot as plt
import numpy as np
# ===================== 1. 遞歸繪制分形樹 =====================
def draw_fractal_tree(x, y, angle, length, depth, ax):
"""
遞歸繪制分形樹
:param x/y: 當(dāng)前起點(diǎn)坐標(biāo)
:param angle: 當(dāng)前分支角度(弧度)
:param length: 當(dāng)前分支長(zhǎng)度
:param depth: 遞歸深度
:param ax: 繪圖軸
"""
if depth == 0:
return
# 計(jì)算分支終點(diǎn)坐標(biāo)
dx = length * np.cos(angle)
dy = length * np.sin(angle)
x2 = x + dx
y2 = y + dy
# 繪制當(dāng)前分支(深度越淺,顏色越綠,線條越粗)
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")
# 遞歸繪制左分支(角度偏轉(zhuǎn)30°,長(zhǎng)度縮短)
draw_fractal_tree(x2, y2, angle + np.pi/6, length * 0.7, depth - 1, ax)
# 遞歸繪制右分支(角度偏轉(zhuǎn)30°,長(zhǎng)度縮短)
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") # 隱藏坐標(biāo)軸
ax.set_facecolor("#f0f0f0") # 淺灰色背景
# 2. 繪制分形樹(起點(diǎn)在底部中間,初始角度向上,遞歸深度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 效果調(diào)整
- 遞歸深度:
depth越大,樹枝越多(建議10~12,太大易卡頓); - 分支角度:修改
np.pi/6(30°),角度越大,樹越“蓬松”; - 長(zhǎng)度縮放:修改
0.7,值越小,分支越短,樹越緊湊; - 顏色:調(diào)整
color的RGB值,比如換成“紅→橙”的秋色調(diào)。
五、進(jìn)階美化技巧
5.1 色彩優(yōu)化
- 避免純黑/純白:用深灰(
#101010)或淺灰(#f8f8f8)作為背景,更柔和; - 漸變配色:用
LinearSegmentedColormap自定義漸變,貼合分形的層次; - 對(duì)數(shù)縮放:繪圖時(shí)用
np.log(img + 1)讓低迭代次數(shù)的色彩過(guò)渡更自然。
5.2 高清輸出
- 設(shè)置
dpi=300(打印級(jí)分辨率),bbox_inches="tight"去除白邊; - 保存為PNG(無(wú)損)或SVG(矢量圖,無(wú)限放大無(wú)鋸齒);
- 對(duì)于超大分辨率(4000×4000+),可分塊計(jì)算避免內(nèi)存溢出。
5.3 動(dòng)態(tài)效果(可選)
結(jié)合matplotlib.animation制作分形演化動(dòng)畫(比如曼德博集合放大過(guò)程):
# 示例:簡(jiǎn)單動(dòng)畫框架(需結(jié)合曼德博代碼)
import matplotlib.animation as animation
fig, ax = plt.subplots(figsize=(10,10))
def update(frame):
# 每次幀調(diào)整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)
六、總結(jié)
核心工具:numpy(數(shù)值)+ matplotlib(繪圖)+ numba(加速)是繪制分形的黃金組合;
美化關(guān)鍵:自定義配色、平滑上色、高清無(wú)白邊輸出,避開默認(rèn)樣式;
分形類型:
- 曼德博/朱利亞集合:適合復(fù)雜精細(xì)的視覺效果,需Numba加速;
- 分形樹:遞歸實(shí)現(xiàn),簡(jiǎn)單易上手,適合入門;
擴(kuò)展方向:可嘗試科赫雪花、謝爾賓斯基三角形,或結(jié)合OpenCV添加濾鏡效果。
通過(guò)以上代碼和技巧,你可以輕松畫出壁紙級(jí)的分形圖,無(wú)論是用于學(xué)習(xí)、可視化還是藝術(shù)創(chuàng)作,都能達(dá)到專業(yè)級(jí)效果。
到此這篇關(guān)于使用python繪制好看的分形圖(附代碼)的文章就介紹到這了,更多相關(guān)python繪制分形圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python自動(dòng)創(chuàng)建Markdown表格使用實(shí)例探究
Markdown表格是文檔中整理和展示數(shù)據(jù)的重要方式之一,然而,手動(dòng)編寫大型表格可能會(huì)費(fèi)時(shí)且容易出錯(cuò),本文將介紹如何使用Python自動(dòng)創(chuàng)建Markdown表格,通過(guò)示例代碼詳細(xì)展示各種場(chǎng)景下的創(chuàng)建方法,提高表格生成的效率2024-01-01
一小時(shí)學(xué)會(huì)TensorFlow2之大幅提高模型準(zhǔn)確率
這篇文章主要介紹了TensorFlow2之大幅提高模型準(zhǔn)確率,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
python3+PyQt5 數(shù)據(jù)庫(kù)編程--增刪改實(shí)例
今天小編就為大家分享一篇python3+PyQt5 數(shù)據(jù)庫(kù)編程--增刪改實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
python文件轉(zhuǎn)為exe文件的方法及用法詳解
py2exe是一個(gè)將python腳本轉(zhuǎn)換成windows上的可獨(dú)立執(zhí)行的可執(zhí)行程序(*.exe)的工具,這樣,你就可以不用裝python而在windows系統(tǒng)上運(yùn)行這個(gè)可執(zhí)行程序。本文重點(diǎn)給大家介紹python文件轉(zhuǎn)為exe文件的方法,感興趣的朋友跟隨小編一起看看吧2019-07-07
給Django Admin添加驗(yàn)證碼和多次登錄嘗試限制的實(shí)現(xiàn)
這篇文章主要介紹了給Django Admin添加驗(yàn)證碼和多次登錄嘗試限制的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

