朱 勤 朱巍峰
(蘇州工業(yè)職業(yè)技術(shù)學(xué)院,江蘇 蘇州 215104)
在自然界中存在著很多自相似的結(jié)構(gòu),按照一定的比例放大或者縮小它的幾何尺寸,整個(gè)結(jié)構(gòu)不會(huì)發(fā)生變化。對(duì)這些幾何結(jié)構(gòu)的研究成為分形幾何學(xué)??坪涨€就是一種典型的分形幾何。
先對(duì)科赫曲線做一個(gè)簡(jiǎn)單的介紹。給定一條水平線段(圖1左)。將它分成三等份,以中間的線段為底邊,向上畫一個(gè)等邊三角形。最后再把中間的線段移除,我們就得到了一個(gè)一階的科赫曲線(如圖1 右)。
如果在一階科赫曲線上的每一個(gè)線段再做一次上述的操作,我們可以獲得二階的科赫曲線(圖2 左)。以此類推,我們可以得到三階的科赫曲線(圖2 右)。這樣的操作理論上可以進(jìn)行無(wú)窮多次。結(jié)合圖1,可以看出,高階的科赫曲線的水平寬度始終是和圖1 左的直線相同,所以我們也可以認(rèn)為圖1 左是一個(gè)零階的科赫曲線。
圖1 一階科赫曲線
圖2 二階和三階科赫曲線
這種曲線最早是海里格·馮·科赫提出的,所以被命名為科赫曲線。由于他的高階曲線和雪花的形狀很相似,所以也被成為雪花曲線。這種曲線的特點(diǎn)是具有自相似性。很容易可以看出這個(gè)曲線的局部和整體的結(jié)構(gòu)是相同的。
在本論文中使用了Python3 語(yǔ)言進(jìn)行編程。這種語(yǔ)言發(fā)展至今已經(jīng)有了一個(gè)龐大的計(jì)算生態(tài),除了本身的開源優(yōu)勢(shì)以外,十四萬(wàn)以上的第三方庫(kù)更是為它提供了無(wú)數(shù)的可能。
Python3的編程環(huán)境有很多,除了Python3 軟件自帶的集成開發(fā)環(huán)境(IDLE)以外,還有Anaconda 和PyCharm 等軟件。不同的軟件只要安裝了相同的庫(kù)都能實(shí)現(xiàn)相同的功能。筆者的編程環(huán)境使用的是Anaconda。
在python3 中要實(shí)現(xiàn)繪圖功能,需要導(dǎo)入相對(duì)應(yīng)的庫(kù)??梢赃x擇的功能庫(kù)有很多,比如turtle 庫(kù)、tkinter 庫(kù)、matplotlib 庫(kù)、seaborn 庫(kù)等。本論文中的科赫曲線只是一個(gè)簡(jiǎn)單的二維圖像,使用turtle 庫(kù)就能夠勝任了。
Turtle 是海龜?shù)囊馑?,所以也被稱為“海龜庫(kù)”,我們可以想象有一只小海龜,在畫板上爬行,它的爬行軌跡就是畫板上繪制的圖像。這只海龜就是我們的畫筆,只要通過(guò)一系列的指令去命令這只小海龜運(yùn)動(dòng),就可以得到我們想要的圖像。常用的“海龜庫(kù)”指令可以控制運(yùn)行方向,移動(dòng)距離,旋轉(zhuǎn)角度,畫筆顏色,畫筆寬度等參數(shù)。使用“海龜庫(kù)”來(lái)繪制科赫曲線是一件非常簡(jiǎn)單而且有趣的事情。
看上去有點(diǎn)復(fù)雜的科赫曲線要用程序來(lái)繪制,會(huì)不會(huì)很復(fù)雜呢?下面給出了繪制科赫曲線的源代碼和部分注釋。
3.2.1 turtle 庫(kù)導(dǎo)入
程序中第一行“import turtle as t”的作用是將turtle 庫(kù)導(dǎo)入到程序中,這樣在程序里面才可以使用turtle 庫(kù)中的相關(guān)指令。為了方便寫代碼,將它的庫(kù)名重命名為t。在下面的程序中以“t.”開頭的指令,都是turtle 庫(kù)中封裝好的功能。編代碼時(shí)只根據(jù)需要調(diào)用相關(guān)函數(shù),設(shè)定相關(guān)參數(shù)就可以了。
3.2.2 科赫曲線繪制函數(shù)
“def DrawKoch(lenght,n):”自定義了一個(gè)功能函數(shù),用來(lái)繪制科赫曲線,調(diào)用它時(shí)需要設(shè)定兩個(gè)參數(shù),其中l(wèi)ength 設(shè)置的是繪制曲線的長(zhǎng)度(參考圖1 左,直線的長(zhǎng)度),它的單位是像素,數(shù)值大小決定了整個(gè)圖形的尺寸。另一個(gè)參數(shù)n 設(shè)置的是科赫曲線的階數(shù),以圖1 和圖2 為例,圖1 左的n 值為0,圖1 右的n值為1。圖2 左的n 值為2,圖2 右的n 值為3。
這個(gè)函數(shù)是本程序的核心。短短的7行代碼,就能夠?qū)崿F(xiàn)繪制科赫曲線。修改參數(shù)length 就可以改變曲線的大小,修改參數(shù)n 就可以改變繪制曲線的階數(shù)。我們先假設(shè)length 為300 像素。然后對(duì)函數(shù)進(jìn)行分析。
函數(shù)中的第一層是一個(gè)if 和else的分支結(jié)構(gòu)。判斷條件是參數(shù)n。當(dāng)n 為0 時(shí),執(zhí)行“t.fd(lenght)”其中“t.fd”的功能是讓“海龜”向前移動(dòng),參數(shù)length 是移動(dòng)的距離,之前我們已經(jīng)假設(shè)為300 像素。很顯然,當(dāng)n 為0 時(shí)繪制出來(lái)的是一個(gè)零階曲線,如圖1 左所示是一條300 像素長(zhǎng)的直線。
當(dāng)我們要畫一階科赫曲線時(shí),將參數(shù)n 設(shè)為1。那么第一層if 和else 分支結(jié)構(gòu)的條件不成立,進(jìn)入else 分支。這個(gè)分支中是一個(gè)循環(huán)結(jié)構(gòu)。用一個(gè)angle 變量去遍歷四個(gè)角度值。具體來(lái)說(shuō)就是這個(gè)循環(huán)結(jié)構(gòu)要執(zhí)行四次,第1 次,angle 為0,第2 次angle 為60,第3 次angle 為-120,第4 次angle 為60。循環(huán)結(jié)構(gòu)的循環(huán)體中有兩條指令,其中“t.left(angle)”是讓“海龜”向左手方向轉(zhuǎn)過(guò)一定角度,角度值由參數(shù)angle 決定。所以每次循環(huán)“海龜”會(huì)先向左轉(zhuǎn)一個(gè)角度,然后再執(zhí)行“DrawKoch(lenght/3,n-1)”指令。這里是這個(gè)函數(shù)中最重要的地方,它調(diào)用了繪制科赫曲線本身。但是長(zhǎng)度變?yōu)樵瓉?lái)的1/3,由于一開始length 設(shè)置為300,所以在這里就變成了100 像素,而且階數(shù)降1,變?yōu)?,相當(dāng)于調(diào)用了函數(shù)“DrawKoch(100,0)”。分析一下可以看到n 為0,所以第一層if 和else的分支結(jié)構(gòu)中判斷條件成立,執(zhí)行“t.fd(100)”。所以此時(shí)調(diào)用“DrawKoch(lenght/3,n-1)”的結(jié)果相當(dāng)于執(zhí)行“t.fd(100)”。
最后我們?cè)倩氐秸麄€(gè)循環(huán)結(jié)構(gòu),可以看出,第1 次循環(huán),“海龜”左轉(zhuǎn)0 度,然后前進(jìn)100 像素。第2 次循環(huán),“海龜”左轉(zhuǎn)60度,然后前進(jìn)100 像素。第3 次循環(huán),“海龜”左轉(zhuǎn)-120 度(即右轉(zhuǎn)120 度),然后前進(jìn)100 像素。第4 次循環(huán),“海龜”左轉(zhuǎn)60 度,然后前進(jìn)100 像素。這里需要說(shuō)明一點(diǎn),“海龜”的初始方向是水平向右的。所以四次循環(huán)的整個(gè)移動(dòng)軌跡就是圖1 右的一階科赫曲線。
如果要繪制二階、三階或者更高階的科赫曲線。只要改變main 函數(shù)的參數(shù)n 就可以了。這個(gè)n 就是科赫曲線的階數(shù)。由于在這個(gè)函數(shù)中用到了遞歸算法,即函數(shù)調(diào)用了自身,而每一次調(diào)用階數(shù)都會(huì)減1,當(dāng)階數(shù)n 變?yōu)? 以后結(jié)束遞歸。比如在本程序中將主函數(shù)參數(shù)設(shè)置為main(300,3),那么繪制出來(lái)的就是3 階科赫曲線,和圖2 右一致。如果要繪制圖2 左這樣的二階科赫曲線,可以把主函數(shù)寫成main(300,2)。
3.2.3 主函數(shù)main
主函數(shù)main 是整個(gè)程序的起點(diǎn),在這里需要對(duì)主要的參數(shù)進(jìn)行設(shè)置。
其中“def main (lenght,n)”定義了一個(gè)主函數(shù)main。和“DrawKoch(lenght,n)”函數(shù)一樣,它也要設(shè)置長(zhǎng)度和階數(shù)兩個(gè)參數(shù)。這個(gè)函數(shù)的主要功能是對(duì)“海龜”進(jìn)行一些設(shè)置。
先用“t.setup(600,600)”指令創(chuàng)建一個(gè)600*600 像素的畫板。然后用“t.speed(0)”指令將“海龜”的爬行速度設(shè)為最大值,如果希望能夠看到圖像的繪制過(guò)程就需要對(duì)“t.speed(0)”中的參數(shù)進(jìn)行設(shè)置了。用“t.pensize(2)”指令將“海龜”的寬度設(shè)為2 像素,如果希望曲線的更加粗,可以將寬度設(shè)置更大的值,但是需要注意曲線長(zhǎng)度和寬度之間的比例。通過(guò)這三條指令為畫圖做好準(zhǔn)備工作。
“t.pu()”是“t.penup()”的縮寫形式,代表將畫筆抬起,可以想象成“海龜”飛了起來(lái),此時(shí)的“海龜”的移動(dòng)不會(huì)再畫板上留下痕跡。然后使用“t.goto(-150,0)”指令讓“海龜”移動(dòng)到畫板中X 坐標(biāo)為-150 像素處。需要說(shuō)明的是畫板的中心點(diǎn)是坐標(biāo)原點(diǎn),X軸正方向水平向右,Y 軸正方向是垂直向上。所以說(shuō)“t.goto(-150,0)”應(yīng)該在畫板中心的左側(cè)150 像素處。這個(gè)起點(diǎn)的位置需要根據(jù)科赫曲線的尺寸以及每次遍歷時(shí)移動(dòng)的距離來(lái)合理的調(diào)整,由于本程序中將length 長(zhǎng)度設(shè)置為300,所以將起點(diǎn)設(shè)置為-150 是比較合理的。如果將length 設(shè)置為400,那么顯然將起點(diǎn)設(shè)置為“t.goto(-200,0)”更為合適。最后再用“t.pd()”也就是“t.pendown()”,它的作用是落筆,讓“海龜”回到畫板上,然后再移動(dòng)“海龜”就能留下爬行的軌跡了。這樣我們就做好了畫圖的準(zhǔn)備。
接下來(lái)調(diào)用了繪制科赫曲線的函數(shù)“DrawKoch(lenght,n)”,進(jìn)行圖形的繪制。繪制完畢以后使用“t.hideturtle()”將“海龜”隱藏。最后使用“t.done()”指令保持畫板,如果不加入“t.done()”指令,在隱藏了“海龜”以后,畫板會(huì)自動(dòng)關(guān)閉,繪制好的圖畫一閃即逝,不利于觀察繪制結(jié)果。
3.2.4 函數(shù)調(diào)用
在整個(gè)程序中用到了兩次def 開頭的指令。分別定義了繪制科赫曲線的函數(shù)以及主函數(shù)。需要說(shuō)明的是函數(shù)在定義以后如果沒(méi)有被調(diào)用是不起作用的。所以在程序的最后調(diào)用主函數(shù)main,同時(shí)設(shè)定長(zhǎng)度為300 像素,階數(shù)為3。運(yùn)行程序以后就能夠得到一個(gè)三階的科赫曲線。
本文設(shè)計(jì)了一個(gè)科赫曲線的繪制程序。這個(gè)程序可以在python3的環(huán)境中直接使用,只要簡(jiǎn)單的修改最后一行主函數(shù)的參數(shù)值,理論上可以實(shí)現(xiàn)任意大小,任意階數(shù)的科赫曲線繪制。
在程序中稍加改動(dòng)可以實(shí)現(xiàn)更多的效果。比如使用“t.screensize()”指令就可以對(duì)畫板參數(shù)進(jìn)行設(shè)置,比如做如下設(shè)置t.screensize(800,600,“black”),其中的“800,600”參數(shù)就是畫板的尺寸,“black”是將畫板背景色設(shè)為黑色。也可以使用“t.pencolor()”指令來(lái)設(shè)置畫筆的顏色。例如使用“t.pencolor(”white“)“將畫筆顏色設(shè)為白色。在本文中的程序中這兩個(gè)參數(shù)都沒(méi)有設(shè)置,則會(huì)使用默認(rèn)值,畫板默認(rèn)為白色,畫筆默認(rèn)為黑色。因此畫出來(lái)的是白底黑線的圖案。
如果我們?cè)趍ain 函數(shù)中“DrawKoch(lenght,n)“指令下方添加“t.right(120)“,這個(gè)指令是向右旋轉(zhuǎn)指令,參數(shù)120 是角度值120度。這樣在畫完一個(gè)科赫曲線以后,會(huì)向右旋轉(zhuǎn)120 度。然后再次調(diào)用“DrawKoch(lenght,n)”,畫第二個(gè)科赫曲線。畫完后再向右轉(zhuǎn)120,然后畫第三個(gè)科赫曲線。這樣就得到黑色夜空下的一片白色雪花。如果在繪圖過(guò)程中改變畫筆的顏色,就可以得到一片彩色的雪花了。
本文中使用的主要編程思路是使用遞歸的算法,讓函數(shù)自己調(diào)用自己,從而用簡(jiǎn)短的程序?qū)崿F(xiàn)了復(fù)雜的繪圖過(guò)程。只要通過(guò)修改函數(shù)中的參數(shù)就可以實(shí)現(xiàn)不同尺寸,不同階數(shù)的曲線繪制。常見的分形幾何圖除了科赫曲線以外,還有謝爾賓斯基三角形、謝爾賓斯基地毯、列維曲線、龍形曲線、分形樹、海岸線等。這些分形圖的繪制也可以用本文提供的程序結(jié)構(gòu)來(lái)實(shí)現(xiàn),只要把繪制程序中的算法部分替換掉就可以了。