# GGB 碎形製作 --- ## 二元樹 Binary Tree ---- ### 定義 二元樹是每個節點最多只有兩個分支的樹狀結構 分支具有左右次序,不能隨意顛倒 二元樹的第 \\(i\\) 層至多擁有 \\(2^{i-1}\\) 個節點 深度為k的二元樹至多總共有 \\(2^{k+1}-1\\) 個節點 ---- 二元樹是圖論中"樹"的特例 樹: 一種無向圖,任一兩點間存在一條簡單路徑 ---- 而本次製作的是完全二元樹 即: 除了最後一層外 每個節點的子節點一定是兩個 ---- ### Outline * 做出初始線段 * 線段集 * 工具 * 迭代 ---- 首先做出任意兩點 A, B 以及 5 個滑桿: * n - 迭代次數 * r1, r2 - 左右線段比例 * a, b - 左右角度 ---- ![](https://i.imgur.com/XKLDvx6.png =70%x) ---- ![](https://i.imgur.com/UpnBIe4.png =70%x) ---- ![](https://i.imgur.com/YkjMHho.png =60%x) 請特別注意那個"度" ---- ![](https://i.imgur.com/SyBGZgJ.png) 做完應該長這樣 ---- 接下來我們開始下幾個指令: ```cpp= u = Vector(A, B) B1 = Translate(B, u) B2 = Dilate(B1, r1, B) B3 = Rotate(B2, alpha, B) B4 = Dilate(B1, r2, B) B5 = Rotate(B4, -beta, B) //特別注意中間那個"-beta" ``` 這些是什麼意思? ---- * Vector 產生起點至終點的向量 * Translate 由起點位置往向量方向延伸做新點 * Dilate 將輸入的向量伸縮 * Rotate 將物件旋轉 ```cpp= // 各參數意義 Vector( <Start Point>, <End Point> ) Translate( <Vector>, <Start Point> ) Dilate( <Object>, <Dilation Factor>, <Dilation Center Point> ) Rotate( <Object>, <Angle>, <Point> ) ``` ---- 最後我們把這些東西作成線段並存成串列 ```cpp= list1 = {Segment(B, B3), Segment(B, B5)} // Segment[ <Point>, <Point> ] ``` 然後把 B1 到 B5 隱藏起來 ---- ![](https://i.imgur.com/RW0gQI9.png =90%x) ---- ![](https://i.imgur.com/oH7OYbx.png =40%x) 接下來要自訂工具 ---- 輸出: ![](https://i.imgur.com/MMw2d1X.png =60%x) ---- 輸入: ![](https://i.imgur.com/J2qduFT.png =60%x) ---- ![](https://i.imgur.com/PvbcWaJ.png =60%x) ---- 來測試一下:D 新增隨意點 C、 D 然後輸入指令: ```cpp= branch(C, D, a, b, r1, r2) ``` ![](https://i.imgur.com/QCq1XGf.png =30%x) ---- 接下來我們要一步步做出最終的式子:D ```cpp= IterationList(Flatten(Sequence(branch(Vertex(Element(list1,i),1),Vertex(Element(list1,i),2),a,b,r1,r2),i, 1,Length(list1))),list1,{{Segment(A,B)}},n) ``` 怕豹 所以先準備這條:D ```cpp= branch(Vertex(Element(list1,i),1),Vertex(Element(list1,i),2),a,b,r1,r2) ``` 什麼意思? ---- * Vertex - 將線段中的點取出 * Element - 將串列中的元素取出 ```cpp= Vertex( <Segment>, <Index> ) Element( <List>, <Position of Element n> ) ``` 所以剛剛那條是 "將 list1 中的第 i 個線段取出,並得到那兩個點" **再拿那兩個點做新線** 可是 GGB 不認識 "i" 是誰ㄚ ---- 所以要來實現一個"迴圈"的功能 加上"Sequence"! ```cpp= Sequence( <Expression>, <Variable k>, <Start Value a>, <End Value b> ) ``` 把這個套上剛剛的那式就變成: ```cpp= Sequence(branch(Vertex(Element(list1,i),1),Vertex(Element(list1,i),2),a,b,r1,r2),i,1,Length(list1)) ``` 最後把產生的串列攤平: ```cpp= Flatten(Sequence(branch(Vertex(Element(list1,i),1),Vertex(Element(list1,i),2),a,b,r1,r2),i,1,Length(list1))) ``` ---- 今天的主角: IterationList ``` IterationList( <Expression>, <Variable Name>, ..., <Start Values>, <Number of Iterations> ) ``` 我們以 list1 作為迭代物件,點 A、B 作為初始值 ```cpp= IterationList( <Expression>, list1, {{Segment(A,B)}},n) ``` ---- 再把上面那一長串指令帶入 expression: ```cpp= IterationList(Flatten(Sequence(branch(Vertex(Element(list1,i),1),Vertex(Element(list1,i),2),a,b,r1,r2),i,1,Length(list1))), list1, {{Segment(A,B)}},n) ``` 就大功告成啦:D ---- ![](https://i.imgur.com/YkPLRA1.png =90%x) :D --- ## 謝爾賓斯基三角形 Sierpinsky Triangle ---- ### 何謂謝爾賓斯基三角形 ? ---- #### 構作(方法) - 去掉中心 - Chaos Game - L系統(接近謝爾賓斯基三角形) ---- #### 1.去掉中心 1. 取一實心三角形。(多數使用等邊三角形) 2. 沿三邊中點連線,將之分成四個小三角形。 3. 去掉中間的那一個小三角形。 4. 對其餘三個小三角形重複1。 > 取一正方形或其他形狀開始,用類似方法,形狀也會和謝爾賓斯基三角形相近 > ![](https://i.imgur.com/jbWhGc7.png) ---- #### 2.用隨機的方法(Chaos Game) 1. 任意取平面上三點A,B,C,組成一三角形 2. 任意取三角形ABC內的一點P,畫出 該點 3. 畫出 P和三角形其中一個頂點的中點 4. 重複1 ---- #### 3.L系統 - 這條曲線以L系統來記述為: - 變數: A , B - 常數: + , - (+為右轉60° -為 左轉60°) - 公理: A - 規則:1. (A → B-A-B) 2.(B → A+B+A) 3.(A,B:向前) ![](https://i.imgur.com/zaJW8I7.png =30%x) ---- ## 開始做謝爾賓斯基三角形吧! ---- ### Outline * 三角形 * 往內縮放 * 迭代 ---- 首先做出任意兩點 A、B 並使用 polygon() 函數做出正三角形 ```cpp= poly1 = Polygon(A, B, 3) // 後面的 3 代表三邊的正多邊形 ``` ---- ![](https://i.imgur.com/SvXWFT8.png) ---- * Vertex - 將線段中的點取出 * Dilate 將輸入的向量伸縮 * Zip(): 將 expression 內的變數以 list 內的替換 ```cpp= // 各參數意義 Vertex( <Segment>, <Index> ) Dilate( <Object>, <Dilation Factor>, <Dilation Center Point> ) Zip( <Expression>, <Var1>, <List1>, <Var2>, <List2>, ...) e.g.: Dilate(poly1,0.5,O) // 以 0.5 倍縮放 poly1 ``` ---- 接下來使用剛剛有提到的 Dilate() 來縮放三角形 並使用 Zip() 各對三頂點縮放 ```cpp= Zip(Dilate(poly1,0.5,O),O,{Vertex(poly1)}) // 將 {vertex(poly1)} 中的點代進 O ``` ---- ![](https://i.imgur.com/w4cE0se.png) ---- 接下來就可以來迭代啦 下一種迭代技巧: Iteration() * Iteration: 將上一層得出的結果輸入到下一層中 ```cpp= Iteration( <Expression>, <Variable Name>, ..., <Start Values>, <Number of Iterations> ) ``` ---- ![](https://i.imgur.com/Q3XzMzB.png) ---- ```cpp= Iteration(Join(Zip(Zip(Dilate(poly1,0.5,O),O,{Vertex(poly1)}),poly1,p1)),p1,{{Polygon(A,B,3)}},n) ``` 1. 以 {{Polygon(A, B, 3)}} 作為初值 2. 用 Zip() 將大括號內的串列取出 3. 放入 expression ---- ![](https://i.imgur.com/7knl3OB.png) --- ## 科赫雪花 Koch Snowflake ---- ### 何謂科赫雪花(Koch Snowflake)? ---- #### 科赫雪花的定義(作法) 1. 任取兩點 A、B,連成一條線段。 2. 以線段 AB 為底邊,做出一個**正三角形** ABC。 3. 以此正三角形 ABC 三邊分為**三等分**,以每一邊的**中間**那等分為底邊,向外做出正三角形,並**去除**正三角形 ABC 的邊。 4. 如此反覆,所產生的圖形與雪花相似,因此稱為科赫"雪花"。 ---- #### 科赫雪花的邊長&周長 假設正三角形 ABC 的邊長為 \\(a\\),周長為 \\(3a\\) 那麼迭代一次後的圖形(十二邊形)邊長為 **\\(a \over 3\\)** 周長為 \\(4a\\),**為原本圖形的 \\(4 \over 3\\) 倍** 。 ---- ## 開始作科赫雪花吧! ---- ### Outline * 任取兩點 * 自訂工具 * 迴圈 * 限制條件 ---- ### Step 1 ## 自訂工具 ---- 首先任取兩點 A, B 然後使用 Dilate() 進行向量伸縮 Rotate() 進行旋轉 ```cpp= C = Dilate(B, 1 / 3, A) //對向量 AB 縮為原本的 1/3 倍。 D = Dilate(B, 2 / 3, A) E = Rotate(D, 60°, C) //以點 C 為旋轉中心,對點 D 逆時針旋轉 60 度。 ``` ---- 輸入好後,圖形長這樣。 ![](https://i.imgur.com/KzNzI4K.png) ---- 然後輸入: ```cpp= {A, C, E, D, B} ``` 把點通通輸進一個串列裡。 ---- 接著,自訂工具。 ![](https://i.imgur.com/RpFOn2A.png =42%x) ---- 輸出物件:剛剛新建的串列 \\(list1\\) ![](https://i.imgur.com/UPc5liL.png =65%x) ---- 輸入:A、B 兩點 ![](https://i.imgur.com/Ikzv5IP.png =65%x) ---- 工具名稱:koch ![](https://i.imgur.com/eZFH1JZ.png =70%x) ---- 完成後,把它們通通隱藏。 ---- Step 1 完成囉! ---- ### Step 2 ## 使用 koch() 輸出科赫雪花圖形 ---- 任取兩點 F, G,然後輸入: ```cpp= H = rotate(F, 60°, G) A1 = {F, H, G} B1 = Polyline(F, H, G, F) //做出線段 FH, HG, GF。 A2 = koch(F, H) ``` ---- 此時的圖形長這樣。 ![](https://i.imgur.com/8kzLswu.png =70%x) ---- **常見錯誤示範** 若輸出的圖形長這樣(新三角形跑到裡面了) 那麼請把 A2 = koch(F, H) 指令中的兩點順序**交換**。 ![](https://i.imgur.com/DsZmO0b.png =50%x) ---- 然後輸入: ```cpp= A3 = Join(Sequence(koch(Element(A2,i),Element(A2,i+1)),i,1,Length(A2)-1)) ``` ---- #### 為什麼指令要這樣寫呢? ---- 我們層層往內推進 ```cpp= Join() -> 把輸出的元素合成成一個串列。 Sequence( <運算式>, <變數名>, <起始值>, <結束值>, <遞增值(沒寫=1)> ) Element( <list1>, i) -> 將串列中的第 i 個元素取出。 ``` ---- A3 輸入完後,圖形長這樣。 ![](https://i.imgur.com/S8ws3gs.png =65%x) ---- 接著,進入試算表。 ![](https://i.imgur.com/omOTdot.png =32%x) ---- 選取 A3 儲存格 把儲存格下拉拖曳到 A6 (A7 以後程式可能當機) 完成請先**存檔** 否則可能會發生 GGB 當機的慘劇。 ![](https://i.imgur.com/LGiGbNH.png =65%x) ---- 選取 A1~A6 儲存格 並按右鍵點擊 屬性 -> 一般 把「顯示物件」關掉。 ![](https://i.imgur.com/sYJT2lU.png =80%x) ---- 接著,輸入: ```cpp= B2 = Polyline(A2) //把 A2 輸出的那些點連成線段。 ``` 完成後一樣到試算表中,下拉 B2 儲存格到 B6。 ---- 然後再用與剛剛 A2~A6, B2~B6 相同方法把 A12~A16, B12~B16(線段 HG) A22~A26, B22~B26(線段 GF) 做出來吧! ```cpp= A12 = koch(H, G) A13 = Join(Sequence(koch(Element(A12,i),Element(A12,i+1)),i,1,Length(A12-1))) B12 = Polyline(A12) A22 = koch(G, F) A23 = Join(Sequence(koch(Element(A22,i),Element(A22,i+1)),i,1,Length(A22-1))) B22 = Polyline(A22) ``` ---- 最後,把所有物件通通隱藏起來 並新增一個滑杆,參數如圖所示。 ![](https://i.imgur.com/928ojIr.png =65%x) ---- 點選儲存格->屬性->進階 - 把 B1 顯示物件的條件設為 n = 1 - 把 B2, B12, B22 顯示物件的條件設為 n = 2 - 把 B3, B13, B23 顯示物件的條件設為 n = 3 - 以此類推 ---- 回到繪圖區,拖動滑杆,如果可以輸出這樣的圖形,那麼 Step 2 就完成啦! ![](https://i.imgur.com/UhP8jxG.png =50%x) ---- ### Step 3 ## 使用遞迴關係式算出科赫雪花周長 & 邊長 ---- 科赫雪花每多一層,周長會變原本的 \\(4 \over 3\\)倍,下列為計算**周長**的方法: ```cpp= a = distance(F, H) f1(a) = 4/3*a 然後把f1(a)點隱藏 f2 = iteration(f1, 3a, n-1) text1 = "周長="+f2+"" ``` ---- 科赫雪花每多一層,邊長會變原本的 \\(1 \over 3\\)倍,下列為計算**邊長**的方法: ```cpp= f3(a) = 1/3*a 然後把f3(a)點隱藏 f4 = iteration(f3, a, n-1) text2 = "邊長="+f4+"" ``` ---- 完成後像是這樣,就大功告成囉! ![](https://i.imgur.com/qEqS2eX.png =50%x) --- ## 謝爾賓斯基地毯 Sierpinski carpet ---- ### 何謂謝爾賓斯基地毯 ? ---- #### 定義 將一實心正方形劃分為 \\(3 \times 3\\) 的 \\(9\\) 個小正方形 去掉中間的小正方形,再對餘下的小正方形重複此操作。 ---- ## 開始做謝爾賓斯基地毯吧! ---- ### Outline - 作正方形 OABC - 滑桿 - 迴圈 ---- ### Step 1 ### 以 OA 為邊作正方形 OABC ---- 設點OA ```python= O=(0,0) A=(27,0) ``` ---- 以 OA 為邊作正方形 OABC 並作出內部的正方形 ```python= O=(0,0) A=(27,0) poly1= Polygon(O, A, 4) //以 OA 為邊作正方形 OABC poly2=Polygon(Dilate(O,((2)/(3)),B),Dilate(A,((2)/(3)),C),4) //第四行為作出內部的正方形 ``` ![](https://i.imgur.com/UfjqGxn.png =30%x) ---- ### Step2 ## 製作滑桿 ---- 命名為整數滑竿 n,範圍 0~5,增量 1 (超過5電腦會爆掉www) ![](https://i.imgur.com/Cff7A03.png) ---- poly2 設定 n>= 1 時才出現 ![](https://i.imgur.com/3ZWZiwt.png =130%x) ---- ### step 3 ### 迴圈畫正方形 ---- ### 指令長這樣,為什麼呢? ![](https://i.imgur.com/g3sr4Qy.png) ---- ### 首先來看這個 變量 i 控制 x 方向,變量 j 控制 y 方向 共兩個迴圈 ![](https://i.imgur.com/8PH5bKI.png) ![](https://i.imgur.com/51AOeDS.png =40%x) ---- ### 再看這個 ![](https://i.imgur.com/jjoKqrH.png) ![](https://i.imgur.com/t1Bxroz.png =40%x) ---- ### 最後再看這個 ![](https://i.imgur.com/KVKcVWt.png) ![](https://i.imgur.com/1rQQWnz.png =40%x) ---- ### 藉由上述我們可知有規律 - 邊長每次乘以 \\(1 \over 3\\) - 起始點 (3,3) 每次乘以 \\(1 \over 3\\) ![](https://i.imgur.com/8PH5bKI.png) ![](https://i.imgur.com/jjoKqrH.png) ![](https://i.imgur.com/KVKcVWt.png) ---- ### 應此將上述改寫成以下這行 外面再套ㄧ層Sequence,加入變量k 執行1~n次(n就是滑桿的值) ![reference link](https://i.imgur.com/g3sr4Qy.png) ```cpp= list1=Sequence(Sequence(Sequence(Polygon((((3)/(3^(k-1)))+((9)/(3^(k-1))) i,((3)/(3^(k-1)))+((9)/(3^(k-1))) j),(((6)/(3^(k-1)))+((9)/(3^(k-1))) i,((3)/(3^(k-1)))+((9)/(3^(k-1))) j),4),i,0,3^(k)-1),j,0,3^(k)-1),k,1,n) ``` ---- ### 這樣就完成了 ─=≡Σ((( つ•̀ω•́)つ ![](https://i.imgur.com/ziPANlX.png =40%x) ###### 全部程式碼在下頁 ---- 全部程式碼在這 ```cpp= O=(0,0) A=(27,0) poly1=Polygon(O,A,4) poly2=Polygon(Dilate(O,((2)/(3)),B),Dilate(A,((2)/(3)),C),4) f=Segment(O,A,poly1) list1=Sequence(Sequence(Sequence(Polygon((((3)/(3^(k-1)))+((9)/(3^(k-1))) i,((3)/(3^(k-1)))+((9)/(3^(k-1))) j),(((6)/(3^(k-1)))+((9)/(3^(k-1))) i,((3)/(3^(k-1)))+((9)/(3^(k-1))) j),4),i,0,3^(k)-1),j,0,3^(k)-1),k,1,n) ```
{"metaMigratedAt":"2023-06-16T21:28:01.338Z","metaMigratedFrom":"YAML","title":"GGB 碎形製作","breaks":true,"description":"GeoGebra 碎形製作,包含二元樹、謝爾賓斯基三角形、科赫雪花以及謝爾賓斯基地毯。","image":"https://i.imgur.com/R98AlVg.png","contributors":"[{\"id\":\"069820a6-3e96-4d49-99f2-2503b2c47d84\",\"add\":34997,\"del\":30751},{\"id\":\"99b4fe46-4693-43e5-8805-c6e091fb73c2\",\"add\":3862,\"del\":762},{\"id\":\"6b0f2d5c-f567-443d-91b0-8b7cc1d3239b\",\"add\":4224,\"del\":1488},{\"id\":\"4658b340-335b-45ca-8bc6-bb239fbfaf88\",\"add\":53,\"del\":1}]"}
    1131 views