對比python學julia(第二章)--(第二節)勾股樹—分形之美

2.1.問題描述

  二話不說,先上圖:

              圖一、勾股定理圖形                                                          圖二、勾股樹

                      

  怎麼樣?是不是很漂亮?勾股樹是根據勾股定理繪製的可以無限重複的圖形,重複多次之後呈現爲樹狀。據說勾股樹最早是由古希臘數學家畢達哥拉斯繪製,因此又稱之爲畢達哥拉斯樹。這種圖形在數學上稱爲分形圖,它們中的一個部分與其整體或者其他部分都十分相似,分形體內任何一個相對獨立的部分,在一定程度上都是整體的再現和縮影。這就是分形圖的自相似特性。

  我國古代把直角三角形稱爲勾股形,並且直角邊中較小者爲勾,另一長直角邊爲股,斜邊爲弦,所以把這個定理稱爲勾股定理。

  公元前 6 世紀,古希臘數學家畢達哥拉斯證明了勾股定理,因而西方人都習慣地稱這個定理爲畢達哥拉斯定理。

  勾股定理的定義:在平面上的一個直角三角形中,兩個直角邊邊長的平方加起來等於斜邊長的平方。

  用數學語言表達爲a2+b2=c2,用圖形表達如上圖一所示。

  以圖一中的勾股定理圖爲基礎,讓兩個較小的正方形按勾股定理繼續“生長”,又能畫出新一代的勾股定理圖,如此一直畫下去,最終得到一棵完全由勾股定理圖組成的樹狀圖形(見圖二) ,稱其爲勾股樹再恰當不過。

  下面我們用Python和Julia分別繪製勾股樹分形圖

2.2.算法分析

  利用分形圖的自相似特性,先構造出分形圖的基本圖形,再不斷地對基本圖形進行復制,就能繪製出分形圖。 針對勾股樹分形圖,其繪製步 如下:

  (1) 先畫出圖一所示的勾股定理圖形作爲基本圖形,將這一過程封裝爲一個繪圖函數,以便迸行遞歸調用。

  (2) 在繪兩個小正方形之前,分別以直角三角形兩條直角邊作爲下一代勾股定理圖形中直角三角形的斜邊以遞歸方式調用繪圖函數畫出下一代的基本圖形。

  (3) 重複執行前兩步,最終可繪出一棵勾股樹的分形圖。由於是遞歸調用,需要遞歸的終止條件,這裏設置爲某一代勾股定理圖的直角三角形的斜邊小於某個數值時就結束遞歸調用。

  如圖三所示,這是一棵經典勾股樹分形圖的繪製過程,可以看到它從一個勾股定理圖開始,逐步成長爲一棵茂盛的勾股樹。

圖三 經典勾股樹繪製過

 

2.3.編程解題

  Python語言內置了一個繪圖模塊”海龜繪圖(Turtle Graphics)”,非常適合繪製勾股樹。

  海歸繪圖模塊是早期的 LOGO 編程語言在 Python 語言中的實現。使用這個模塊繪圖時,可以把屏幕當成一塊畫布,通過控制一個小三角形(或小海龜)的畫筆在畫布上移動 從而在它前進的路徑上繪製出圖形。這和 Scratch 中畫筆的功能類似。

  海歸繪圖(turtle) 模塊提供一套用於繪圖的函數,在使用之前要先導人 turtle 模塊。

  打開 IDLE環境,在 Python Shell 窗口中使用 import 語句導人 turtle 模塊:

  >>> import turtle

  輸人下面一行代碼:

  >>> turtle.fd(100)

  這時會出現一個標題爲 Python Turtle Graphics 的窗 口 ,在窗口中央有一個小三角形圖標向右移動並畫出一條直線,如圖四所示。

  如果看不到這個窗口,可能是被 Python Shell 窗口遮擋住了。

圖四

  如果不想每次都用turtle.fd(100)這種方式,可以用下面的方式,就簡潔很多:

  >>> from turtle import *

  >>> fd(100)

  關於海龜繪圖模塊的畫布座標系統、畫筆運動控制、畫筆設置等,可以參考原書或網上查詢相關資料,這裏不再贅述。

      讓我們來看看最終的Pyton代碼:

 

 1 '''
 2 程序:繪製勾股樹
 3 作者:蘇秦@小海豚科學館公衆號
 4 來源:圖書《Python趣味編程:從入門到人工智能》
 5 '''
 6 from turtle import *
 7 from math import cos, radians
 8 
 9 def square(b):
10     '''畫正方形'''
11     for i in range(4):
12         fd(b)
13         right(90)
14 
15 def draw(b):
16     '''畫勾股樹'''
17     if b < 50: return
18     
19     square(b)
20         
21     fd(b)
22     left(30)
23     draw(b * cos(radians(30)))
24     square(b * cos(radians(30)))
25 
26     right(90)
27     fd(b * cos(radians(30)))
28     draw(b * cos(radians(60)))
29     square(b * cos(radians(60)))
30     
31     right(90)
32     fd(b * cos(radians(60)))
33     right(30)
34     fd(b)
35     right(90)
36     fd(b)
37     right(90)
38         
39 if __name__ == '__main__':
40     speed(0)
41     up()
42     goto(50, -250)
43     down()
44     seth(90)
45     draw(100)

   不幸的是,julia語言沒有內置類似海龜繪圖的模塊,不過好在已經有人提供了第三方庫實現類似海龜繪圖的功能。這個第三方庫就是Luxor,並且是開源的,開源地址在這裏:https://github.com/JuliaGraphics/Luxor.jl

  Luxor是繪製簡單靜態矢量圖形的Julia包,它提供了用於處理形狀、多邊形、剪切蒙版、PNG和SVG圖像、海龜圖形和簡單動畫的基本繪圖功能和實用工具。以上是Julia開發文檔中的介紹原話(當然,原話是英文的),筆者覺得已經非常清晰全面,就原文照搬過來了。

  首先用我們之前介紹的方法安裝Luxor包,當然最簡單的方法就是在REPL環境下輸入:using Luxor,如果沒有安裝Luxor包,編程環境會提示你沒有安裝該包,是否要安裝,輸入y,接下來跟着提示操作,就能順利安裝Luxor包。

  然後我們來看一個例子:

1 using Luxor
2 Drawing(500, 500, "my-drawing.svg")
3 origin()
4 setcolor("red")
5 circle(Point(0, 0), 100, :fill)
6 finish()
7 preview()

  這段簡短的代碼完成以下工作:

  • 繪製一個500單位的正方形(通常我們稱之爲畫布),並以SVG格式保存在“my-drawing.svg”中。
  • 將零點從左上角移動到中心。圖形引擎通常從左上角開始測量(偶爾從左下角開始),但如果從中心開始,則更容易計算出物體的位置。
  • origin()函數將0/0點(座標原點)移動到圖形的中心。
  • 選擇200種左右顏色中的一個(在colors .jl中定義)。
  • 以x = 0, y = 0爲圓心繪製一個半徑爲100個單位的圓,並用當前的顏色填充它。
  • 完成繪製並在屏幕上顯示它。(筆者注:Luxor沒有圖形界面,它通常打開操作系統默認瀏覽器顯示svg文件,默認圖像軟件顯示PNG圖片等,並且該功能只在REPL環境下有效)。

  關於Luxor更多的繪圖知識,不在本文的討論範圍內。這裏重點介紹在Luxor中包含的海龜繪圖模塊。

  Luxor提供了一些基本的“海龜圖形”功能。控制海龜:向前、轉彎、圓形、方向、朝向、矩形、向下、向上、筆畫顏色、筆畫寬度和重新定位,等等,並且角度以度而不是弧度爲單位(這一點與Python的海龜繪圖模塊不同)。

  定義一個海龜對象是這樣的:turtle=Turtle().而下面的代碼將繪製一條直線:

using Luxor
turtle=Turtle()
Forward(turtle,100)
finish()

  以下是海龜繪圖模塊的動作函數:

 

海龜繪圖函數

對應動作

Forward

More forward by d units

Turn

Increase the turtle's rotation by n degrees

Circle

Draw filled circle centered at current pos

HueShift

Shift the Hue of the turtle's pen color by n

Message

Output text

Orientation

Set the turtle's orientation to n degrees

Pen_opacity_random

Set opacity to random value

Pencolor

Set the Red, Green, and Blue values

Pendown

Start drawing

Penup

Stop drawing

Penwidth

Set the width of the line to n

Pop

Move turtle to the value stored on the stack

Push

Save the turtle's position on the stack

Randomize_saturation

Randomize the saturation of the current color

Rectangle

Draw filled rectangle centered at current pos

Reposition

Place turtle at new position

Towards

Rotate turtle to face towards a point

  具體函數的參數大家可以到這個網址查閱:http://juliagraphics.github.io/Luxor.jl/dev/howto/turtle/

  據此我們可以用Luxor的海龜繪圖模塊來繪製勾股樹了。打開VSCode,新建ggs.li,輸入代碼如下:

 1 =#
 2 using Luxor
 3 
 4 function square(turtle::Turtle,b)
 5     #畫正方形
 6     for i in 1:4
 7         Forward(turtle,b)
 8         Turn(turtle,-90)
 9     end
10 end
11 
12 function draw(turtle::Turtle,b)
13     #畫勾股樹'''
14     if b < 5
15         return
16     end
17     square(turtle,b)
18         
19     Forward(turtle,b)
20     Turn(turtle,30)
21     draw(turtle,b * cosd(30))
22     square(turtle,b * cosd(30))
23 
24     Turn(turtle,-90)
25     Forward(turtle,b * cosd(30))
26     draw(turtle,b * cosd(60))
27     square(turtle,b * cosd(60))
28     
29     Turn(turtle,-90)
30     Forward(turtle,b * cosd(60))
31     Turn(turtle,-30)
32     Forward(turtle,b)
33     Turn(turtle,-90)
34     Forward(turtle,b)
35     Turn(turtle,-90)
36 end
37 
38 function main()
39     Drawing(1000, 800, "ggs.svg")
40     origin()
41     turtle=Turtle()
42     #@svg begin
43         draw(turtle,100)
44     #end
45     finish()
46 end
47 main()

  這段代碼會在代碼文件同目錄下生成ggs.svg文件,並繪製一幅勾股樹圖形。

 

 

   對比Python代碼,我們可以發現它們之間沒有多大差別。有一點要注意,Python的三角函數的參數是弧度,所以要用radians函數將度轉換爲弧度(如:cos(radians(30))),而julia分cos()、cosd()兩類函數,前者的參數是單位是弧度,後者的參數單位是度。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章