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