遞歸三部曲:
〇、介紹遞歸及三原則
一、謝爾賓斯基三角形
二、漢諾塔
三、迷宮探索
1、謝爾賓斯基三角形
本教程爲本人在b站投稿的視頻教程對應的文字版
視頻較詳細,文本較簡潔,大家選擇一個看就好
謝爾賓斯基三角形(Sierpinski triangle),
是一種如圖所示的分形
點擊觀看謝爾賓斯基三角形繪製動畫(Sierpinski Triangle)
這裏分析一下其繪製過程
1、繪製一個三角形
2、取三角形的三邊中點,
以每兩個中點及其所在邊共有的頂點,
三個一組,組合成三個小三角形,
對三個三角形進行繪製,
如下圖所示,分別爲ADF,DBE,FEC。
3、對2中的三個小三角形,重複步驟2。
理論上,步驟二可以無限重複下去,但是代碼而言,要保證算法的有窮性,所以一般都是將步驟二運行有限次數之後結束。
2、結合遞歸三原則編寫代碼
首先,需要添加方法draw_sierpinski
來繪製指定層數的謝爾賓斯基三角形
def draw_sierpinski(triangle,depth):
"""
繪製指定層數的謝爾賓斯基三角形
triangle: 指定三角形三個頂點座標,示例:((ax,ay),(bx,by),(cx,cy))。
depth: 指定層數
"""
這裏再解釋下depth這個層數的具體意思,即上面的步驟二執行的次數
那麼下圖從左到右,層數分別爲:0,1,2,3,4
然後我們再分析下draw_sierpinski
方法應該怎麼寫
1,繪製三角形triangle
2,如果層數depth爲0則退出遞歸
3,取三角形中點,及對應形成的三個小三角形,
4,對這三個三角形,分別再次調用draw_sierpinski方法,
繪製depth-1的謝爾賓斯基三角形
爲了實現上面四步,我們需要添加兩個輔助方法
get_midpoint(a,b)
: 返回a,b兩點的中點座標
draw_triangle(a,b,c)
: 以a,b,c爲頂點繪製三角形
最終代碼如下
#!/usr/bin/python3
import turtle
t = turtle.Turtle()
def get_midpoint(a, b):
ax, ay = a
bx, by = b
return (ax + bx) / 2, (ay + by) / 2
def draw_triangle(a, b, c):
ax, ay = a
bx, by = b
cx, cy = c
t.penup()
t.goto(ax, ay)
t.pendown()
t.goto(bx, by)
t.goto(cx, cy)
t.goto(ax, ay)
t.penup()
def draw_sierpinski(triangle, depth):
"""
:param triangle: 指定三角形三個頂點座標,示例:((ax,ay),(bx,by),(cx,cy))。
:param depth: 指定層數
"""
a, b, c = triangle
draw_triangle(a, b, c)
if depth == 0:
return
else:
d = get_midpoint(a, b)
e = get_midpoint(b, c)
f = get_midpoint(c, a)
draw_sierpinski([a, d, f], depth-1)
draw_sierpinski([d, b, e], depth-1)
draw_sierpinski([f, e, c], depth-1)
if __name__ == '__main__':
triangle = [[-200, -100], [0, 200], [200, -100]]
draw_sierpinski(triangle, 5)
turtle.done()
運行效果如圖
然後我們結合代碼回顧一下遞歸三原則
如圖所示:
這裏補充一點,遞歸三原則的2和三常常是一起進行的,
即在向1中的基礎情況靠攏的的情況下調用遞歸方法本身
3、最後補充:
問題1:沒有顏色的變化
本人在b站的關於這個謝爾賓斯基三角形視頻教程最終代碼如步驟二中所示,但是有個小小的細節問題,就是不同層數的三角形之間沒有顏色的變化,與本文開頭的圖一不符。所以最後還是要添加一下顏色的處理
問題2:最終運行結束後中間有黑點
大家看步驟二重最後運行效果圖,中間有個黑點, 這個黑點是turtle畫筆對象,一般是箭頭樣式,放在中間破壞了整個畫面,隱藏掉就好了
解決問題一二後代碼如下
#!/usr/bin/python3
import turtle
t = turtle.Turtle()
t.hideturtle()
FillColors=[
'#CAE1FF',
'#FFEFDB',
'#8470FF',
'#FF6347',
'#FFDEAD',
'#C1FFC1'
]
def get_midpoint(a, b):
ax, ay = a
bx, by = b
return (ax + bx) / 2, (ay + by) / 2
def draw_triangle(a, b, c, depth):
ax, ay = a
bx, by = b
cx, cy = c
t.penup()
_tcolor = FillColors[depth % len(FillColors)]
t.color("black", _tcolor)
t.goto(ax, ay)
t.pendown()
t.begin_fill()
t.goto(bx, by)
t.goto(cx, cy)
t.goto(ax, ay)
t.end_fill()
t.penup()
def draw_sierpinski(triangle, depth):
"""
:param triangle: 指定三角形三個頂點座標,示例:((ax,ay),(bx,by),(cx,cy))。
:param depth: 指定層數
"""
a, b, c = triangle
draw_triangle(a, b, c, depth)
if depth == 0:
return
else:
d = get_midpoint(a, b)
e = get_midpoint(b, c)
f = get_midpoint(c, a)
draw_sierpinski([a, d, f], depth-1)
draw_sierpinski([d, b, e], depth-1)
draw_sierpinski([f, e, c], depth-1)
if __name__ == '__main__':
triangle = [[-200, -100], [0, 200], [200, -100]]
draw_sierpinski(triangle, 5)
turtle.done()
此時運行效果如圖
問題3:線條重畫導致粗細不一
如果有朋友觀察細緻的話,會發現一些邊線有時有粗細不一的情況。
這是由於每次重複繪製過程的第二步時,邊線也被重複繪製了,但是重複繪製有時候並不一定和原來的邊線是齊的,這種反覆不齊導致整個線條實際上是有多個不完全一致的線段疊起來的,所以看上去會有粗有細。
實際上,步驟二爲了讓大家簡單直接的理解遞歸,所以選擇了重複繪製邊線這種方便遞歸的方法來畫謝爾賓斯基三角形,方便的缺點就是重畫邊線導致不齊的線段疊加,當然這個缺點對於初學者完全可以忽略。
如果我們要解決這個問題,實際上就需要調整我們的繪製過程,修改原來繪製過程中的步驟二,不過這會使遞歸略微麻煩一點點。
具體的可看我以前博客園寫的文章,這篇文章裏就用的是另外一種繪製過程,來避免了重複畫線:https://www.cnblogs.com/BigShuang/p/10837020.html
我就不搬運過來了。
本文主要通過三個實例來幫助大家理解遞歸(其展示動畫已上傳B站):
謝爾賓斯基三角形(Sierpinski Triangle)
漢諾塔(Tower of Hanoi)
迷宮探索(Maze Exploring)
本文代碼已上傳到github:https://github.com/BigShuang/recursion-with-turtle
本文參考文獻:
Problem Solving with Algorithms and Data Structures using Python
turtle官方文檔:
中文:https://docs.python.org/zh-cn/3.6/library/turtle.html
英文:https://docs.python.org/3.6/library/turtle.html