Python繪制分形圖案探索無限細(xì)節(jié)和奇妙之美
分形是無限復(fù)雜的模式,在不同的尺度上具有自相似性。例如,一棵樹的樹干會分裂成更小的樹枝。這些樹枝又分裂成更小的樹枝,以此類推。
通過編程的方式生成分形,可以將簡單的形狀變成復(fù)雜的重復(fù)圖案。
本文將探討如何利用一些簡單的幾何學(xué)基礎(chǔ)和編程知識,在Python中建立令人印象深刻的分形圖案。
分形在數(shù)據(jù)科學(xué)中發(fā)揮著重要作用。例如,在分形分析中,對數(shù)據(jù)集的分形特征進(jìn)行評估,以幫助理解基礎(chǔ)過程的結(jié)構(gòu)。此外,處于分形生成中心的循環(huán)算法可以應(yīng)用于廣泛的數(shù)據(jù)問題,例如從二進(jìn)制搜索算法到遞歸神經(jīng)網(wǎng)絡(luò)。
一、目標(biāo)
寫一個可以畫等邊三角形的程序,并且在三角形的每條邊上,它必須能夠繪制一個稍微小一點(diǎn)的向外的三角形。能夠根據(jù)人的意愿多次重復(fù)此過程,從而創(chuàng)建一些有趣的模式。
二、表示圖像
把圖像表示為一個二維的像素陣列。像素陣列中的每個單元格將代表該像素的顏色(RGB)。
為此,可以使用NumPy庫生成像素數(shù)組,并使用Pillow將其轉(zhuǎn)換為可以保存的圖像。

藍(lán)色像素的x值為3,y值為4,可以通過一個二維數(shù)組訪問,如pixels[4][3]
三、畫一條線
現(xiàn)在開始編碼,首先,需要一個可以獲取兩組坐標(biāo)并在它們之間畫一條線的函數(shù)。
下面的代碼通過在兩點(diǎn)之間插值來工作,每一步都向像素陣列添加新的像素。你可以把這個過程看作是在一條線上逐個像素地進(jìn)行著色。
可以在每個代碼片段中使用連續(xù)字符“\”來容納一些較長的代碼行。
import numpy as np
from PIL import Image
import math
def plot_line(from_coordinates, to_coordinates, thickness, colour, pixels):
# 找出像素陣列的邊界
max_x_coordinate = len(pixels[0])
max_y_coordinate = len(pixels)
# 兩點(diǎn)之間沿著x軸和y軸的距離
horizontal_distance = to_coordinates[1] - from_coordinates[1]
vertical_distance = to_coordinates[0] - from_coordinates[0]
# 兩點(diǎn)之間的總距離
distance = math.sqrt((to_coordinates[1] - from_coordinates[1])**2 \
+ (to_coordinates[0] - from_coordinates[0])**2)
# 每次給一個新的像素上色時,將向前走多遠(yuǎn)
horizontal_step = horizontal_distance/distance
vertical_step = vertical_distance/distance
# 此時,將進(jìn)入循環(huán)以在像素數(shù)組中繪制線
# 循環(huán)的每一次迭代都會沿著線添加一個新的點(diǎn)
for i in range(round(distance)):
# 這兩個坐標(biāo)是直線中心的坐標(biāo)
current_x_coordinate = round(from_coordinates[1] + (horizontal_step*i))
current_y_coordinate = round(from_coordinates[0] + (vertical_step*i))
# 一旦得到了點(diǎn)的坐標(biāo),
# 就在坐標(biāo)周圍畫出尺寸為thickness的圖案
for x in range (-thickness, thickness):
for y in range (-thickness, thickness):
x_value = current_x_coordinate + x
y_value = current_y_coordinate + y
if (x_value > 0 and x_value < max_x_coordinate and \
y_value > 0 and y_value < max_y_coordinate):
pixels[y_value][x_value] = colour
# 定義圖像的大小
pixels = np.zeros( (500,500,3), dtype=np.uint8 )
# 畫一條線
plot_line([0,0], [499,499], 1, [255,200,0], pixels)
# 把像素陣列變成一張真正的圖片
img = Image.fromarray(pixels)
# 顯示得到的圖片,并保存它
img.show()
img.save('Line.png')
此函數(shù)在像素陣列的每個角之間繪制一條黃線時的結(jié)果
四、畫三角形
現(xiàn)在有了一個可以在兩點(diǎn)之間畫線的函數(shù),可以畫第一個等邊三角形了。
給定三角形的中心點(diǎn)和邊長,可以使用公式計算出高度:h = ½(√3a)。
現(xiàn)在利用這個高度、中心點(diǎn)和邊長,可以計算出三角形的每個角的位置。使用之前制作的plot_line函數(shù),可以在每個角之間畫一條線。
def draw_triangle(center, side_length, thickness, colour, pixels):
# 等邊三角形的高度是,h = ?(√3a)
# 其中a是邊長
triangle_height = round(side_length * math.sqrt(3)/2)
# 頂角
top = [center[0] - triangle_height/2, center[1]]
# 左下角
bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]
# 右下角
bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]
# 在每個角之間畫一條線來完成三角形
plot_line(top, bottom_left, thickness, colour, pixels)
plot_line(top, bottom_right, thickness, colour, pixels)
plot_line(bottom_left, bottom_right, thickness, colour, pixels)
在500x500像素PNG的中心繪制三角形時的結(jié)果
五、生成分形
一切都已準(zhǔn)備就緒,可以用Python創(chuàng)建第一個分形。
但是最后一步是最難完成的,三角形函數(shù)為它的每一邊調(diào)用自己,需要能夠計算每個新的較小三角形的中心點(diǎn),并正確地旋轉(zhuǎn)它們,使它們垂直于它們所附著的一側(cè)。
通過從旋轉(zhuǎn)的坐標(biāo)中減去中心點(diǎn)的偏移量,然后應(yīng)用公式來旋轉(zhuǎn)一對坐標(biāo),可以用這個函數(shù)來旋轉(zhuǎn)三角形的每個角。
def rotate(coordinate, center_point, degrees):
# 從坐標(biāo)中減去旋轉(zhuǎn)的點(diǎn)
x = (coordinate[0] - center_point[0])
y = (coordinate[1] - center_point[1])
# Python的cos和sin函數(shù)采用弧度而不是度數(shù)
radians = math.radians(degrees)
# 計算旋轉(zhuǎn)點(diǎn)
new_x = (x * math.cos(radians)) - (y * math.sin(radians))
new_y = (y * math.cos(radians)) + (x * math.sin(radians))
# 將在開始時減去的偏移量加回旋轉(zhuǎn)點(diǎn)上
return [new_x + center_point[0], new_y + center_point[1]]
將每個坐標(biāo)旋轉(zhuǎn)35度的三角形
可以旋轉(zhuǎn)一個三角形后,思考如何在第一個三角形的每條邊上畫一個新的小三角形。
為了實(shí)現(xiàn)這一點(diǎn),擴(kuò)展draw_triangle函數(shù),為每條邊計算一個新三角形的旋轉(zhuǎn)和中心點(diǎn),其邊長被參數(shù)shrink_side_by減少。
一旦它計算出新三角形的中心點(diǎn)和旋轉(zhuǎn),它就會調(diào)用draw_triangle(自身)來從當(dāng)前線的中心畫出新的、更小的三角形。然后,這將反過來打擊同一個代碼塊,為一個更小的三角形計算另一組中心點(diǎn)和旋轉(zhuǎn)。
這就是所謂的循環(huán)算法,因?yàn)?code>draw_triangle函數(shù)現(xiàn)在會調(diào)用自己,直到達(dá)到希望繪制的三角形的最大深度。有這個轉(zhuǎn)義句子是很重要的,因?yàn)槔碚撋线@個函數(shù)會一直循環(huán)下去(但實(shí)際上調(diào)用堆棧會變得太大,導(dǎo)致堆棧溢出錯誤)。
def draw_triangle(center, side_length, degrees_rotate, thickness, colour, \
pixels, shrink_side_by, iteration, max_depth):
# 等邊三角形的高度是,h = ?(√3a)
# 其中'a'是邊長
triangle_height = side_length * math.sqrt(3)/2
# 頂角
top = [center[0] - triangle_height/2, center[1]]
# 左下角
bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]
# 右下角
bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]
if (degrees_rotate != 0):
top = rotate(top, center, degrees_rotate)
bottom_left = rotate(bottom_left, center, degrees_rotate)
bottom_right = rotate(bottom_right, center, degrees_rotate)
# 三角形各邊之間的坐標(biāo)
lines = [[top, bottom_left],[top, bottom_right],[bottom_left, bottom_right]]
line_number = 0
# 在每個角之間畫一條線來完成三角形
for line in lines:
line_number += 1
plot_line(line[0], line[1], thickness, colour, pixels)
# 如果還沒有達(dá)到max_depth,就畫一些新的三角形
if (iteration < max_depth and (iteration < 1 or line_number < 3)):
gradient = (line[1][0] - line[0][0]) / (line[1][1] - line[0][1])
new_side_length = side_length*shrink_side_by
# 正在繪制的三角形線的中心
center_of_line = [(line[0][0] + line[1][0]) / 2, \
(line[0][1] + line[1][1]) / 2]
new_center = []
new_rotation = degrees_rotate
# 需要旋轉(zhuǎn)traingle的數(shù)量
if (line_number == 1):
new_rotation += 60
elif (line_number == 2):
new_rotation -= 60
else:
new_rotation += 180
# 在一個理想的世界里,這將是gradient=0,
# 但由于浮點(diǎn)除法的原因,無法
# 確保永遠(yuǎn)是這種情況
if (gradient < 0.0001 and gradient > -0.0001):
if (center_of_line[0] - center[0] > 0):
new_center = [center_of_line[0] + triangle_height * \
(shrink_side_by/2), center_of_line[1]]
else:
new_center = [center_of_line[0] - triangle_height * \
(shrink_side_by/2), center_of_line[1]]
else:
# 計算直線梯度的法線
difference_from_center = -1/gradient
# 計算這條線距中心的距離
# 到新三角形的中心
distance_from_center = triangle_height * (shrink_side_by/2)
# 計算 x 方向的長度,
# 從線的中心到新三角形的中心
x_length = math.sqrt((distance_from_center**2)/ \
(1 + difference_from_center**2))
# 計算出x方向需要走哪條路
if (center_of_line[1] < center[1] and x_length > 0):
x_length *= -1
# 現(xiàn)在計算Y方向的長度
y_length = x_length * difference_from_center
# 用新的x和y值來偏移線的中心
new_center = [center_of_line[0] + y_length, \
center_of_line[1] + x_length]
draw_triangle(new_center, new_side_length, new_rotation, \
thickness, colour, pixels, shrink_side_by, \
iteration+1, max_depth)
三角形分形,收縮邊=1/2,最大深度=2
六、結(jié)論
下面是通過修改輸入到draw_triangle函數(shù)的shrink_side_by和max_depth值生成的不同圖像的一些示例。
有趣的是,這些多次重復(fù)的圖案往往能創(chuàng)造出更復(fù)雜的形狀,比如六邊形,但卻具有令人著迷的對稱性。

越來越復(fù)雜的形狀開始在重復(fù)三角形的對稱性中出現(xiàn)

另一個分形,每次迭代使用較小的尺寸減小
分形是非常有趣的玩法,可以創(chuàng)造出美麗的圖案。使用一些簡單的概念和豐富的創(chuàng)造力,可以產(chǎn)生非常令人印象深刻的結(jié)構(gòu)。
在理解分形的核心屬性和應(yīng)用循環(huán)算法的過程中打下的堅實(shí)基礎(chǔ),可以幫助理解數(shù)據(jù)科學(xué)中更復(fù)雜的分形問題。
到此這篇關(guān)于Python繪制分形圖案探索無限細(xì)節(jié)和奇妙之美的文章就介紹到這了,更多相關(guān)Python繪制分形圖案內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)使用Keras進(jìn)行簡單分類
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)使用Keras進(jìn)行簡單分類,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
python數(shù)據(jù)分析Numpy庫的常用操作
numpy 是 Python 的一個科學(xué)計算的庫,提供了矩陣運(yùn)算的功能,其一般與 Scipy、matplotlib 一起使用,這篇文章總結(jié)下python數(shù)據(jù)分析Numpy庫的常用操作,感興趣的朋友一起看看吧2022-01-01
python實(shí)現(xiàn)企業(yè)微信定時發(fā)送文本消息的示例代碼
這篇文章主要介紹了python實(shí)現(xiàn)企業(yè)微信定時發(fā)送文本消息的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
python實(shí)戰(zhàn)之Scrapy框架爬蟲爬取微博熱搜
前面講解了Scrapy中各個模塊基本使用方法以及代理池、Cookies池。接下來我們以一個反爬比較強(qiáng)的網(wǎng)站新浪微博為例,來實(shí)現(xiàn)一下Scrapy的大規(guī)模爬取。2021-09-09
使用python/pytorch讀取數(shù)據(jù)集的示例代碼
這篇文章主要為大家詳細(xì)介紹了使用python/pytorch讀取數(shù)據(jù)集的示例,文中的示例代碼講解詳細(xì),具有一定參考價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
Python?實(shí)現(xiàn)循環(huán)最快方式(for、while?等速度對比)
這篇文章主要介紹了Python?利用for、while?實(shí)現(xiàn)循環(huán)最快方式,文章主要對for、while?等速度對比詳細(xì)介紹,具有一定的參考價值?,需要的小伙伴可以參考一下2022-01-01
Python源碼學(xué)習(xí)之PyType_Type和PyBaseObject_Type詳解
今天給大家?guī)淼氖顷P(guān)于Python源碼的相關(guān)知識學(xué)習(xí),文章圍繞著PyType_Type和PyBaseObject_Type展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06

