用Python代码创作分形艺术:Turtle递归绘图实例
本文通过Python标准库Turtle与递归算法,详细讲解科赫雪花、分形二叉树与谢尔宾斯基三角形的实现原理与完整代码,带你掌握自相似分形艺术的生成机制与优化技巧。
分形艺术是通过简单的数学规则与自相似结构生成复杂几何图形的视觉表现形式。借助 Python 内置的 turtle(海龟绘图)模块与递归算法,我们无需安装任何第三方依赖,就能通过控制几何变换与递归深度,将抽象的数学公式直接转化为极具秩序感的矢量艺术作品。本文将从递归思想与海龟运动的对应关系切入,带你一步步构建科赫雪花、分形二叉树与谢尔宾斯基三角形,并掌握提升绘图性能与避免递归栈溢出的工程技巧。
一、分形与递归:核心概念与运行机制
分形(Fractal)的核心特征在于“自相似性”,即物体的局部与整体在结构形态上高度一致。无论放大多少倍,局部都会呈现出与整体类似的精巧结构。在编程中,这种“自身包含自身”的数学定义,天然契合计算机科学中的递归算法。
使用 Python 的 turtle 模块绘制分形时,递归函数主要承担两个职责:
- 基线条件(Base Case):当递归深度降低到
0时,函数不再继续拆分,而是执行具体的绘图动作(例如绘制一条固定长度的直线)。若缺少基线条件,程序将因无限循环而引发RecursionError。 - 递归调用与位姿变换:在递归深入时,海龟不仅要缩小行进步长,还要通过
left(angle)和right(angle)调整角度,随后调用自身完成子结构的绘制。
为了让递归过程清晰稳定,通常建议遵循“位置对称与状态对齐”原则:即在绘制树枝等需要回溯的图形时,子函数执行完毕后,应让海龟恢复到调用前的绝对坐标与朝向角度。
二、实例一:经典科赫雪花(Koch Snowflake)
科赫曲线的构建规则非常简练:将一条长度为 $L$ 的线段三等分,将中间三分之一的线段替换为一个等边三角形的两条边(去掉底边),此时原线段由 4 条长度为 $L/3$ 的小线段组成。将这一规则连续迭代应用,并在一个等边三角形的三条边上分别执行,最终便构成了闭合的科赫雪花。
以下是使用 Python 绘制完整科赫雪花的代码实现:
import turtle
def draw_koch(t, length, order):
"""
绘制单条科赫曲线
:param t: Turtle 实例
:param length: 当前线段长度
:param order: 递归阶数
"""
if order == 0:
t.forward(length)
else:
new_len = length / 3.0
# 4 次子递归,结合角度变换
draw_koch(t, new_len, order - 1)
t.left(60)
draw_koch(t, new_len, order - 1)
t.right(120)
draw_koch(t, new_len, order - 1)
t.left(60)
draw_koch(t, new_len, order - 1)
def main():
screen = turtle.Screen()
screen.setup(width=800, height=800)
screen.bgcolor("#F8F7F3")
screen.title("科赫雪花分形实例")
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
screen.tracer(0, 0) # 关闭自动刷新,大幅提升渲染性能
t.pencolor("#30363C")
t.pensize(1.5)
# 移动到画面上方正中,准备绘制封闭三角形
t.penup()
t.goto(-200, 120)
t.pendown()
# 三条科赫曲线组成雪花整体
side_length = 400
recursion_depth = 4
for _ in range(3):
draw_koch(t, side_length, recursion_depth)
t.right(120)
screen.update()
screen.mainloop()
if __name__ == "__main__":
main()
三、实例二:自相似分形二叉树
分形二叉树不仅是计算机图形学中的经典算法题,常用于模拟真实自然界中的树木、血管与河流分支。其规律是:绘制主干后,向左偏转一定角度生成左子树,然后向右偏转生成右子树,子树长度以比例因子(如 0.75)逐步衰减。
二叉树绘制的关键在于状态回溯(Backtracking)。海龟在绘制完左子树后,位置停留在树梢,必须反向倒退回到分叉节点,才能准确绘制右子树;绘制完右子树后,再倒退回到父节点。这样每次递归调用结束时,海龟都会保持坐标和朝向不变。
import turtle
def draw_tree(t, branch_len, angle, ratio, order):
"""
递归绘制分形树
:param t: Turtle 实例
:param branch_len: 当前树枝长度
:param angle: 分叉角度
:param ratio: 长度衰减系数 (0 < ratio < 1)
:param order: 当前递归阶数
"""
if order == 0 or branch_len < 3:
return
# 动态根据深度调整笔触粗细与颜色
t.pensize(max(1.0, order * 1.2))
if order <= 2:
t.pencolor("#B96555") # 树梢叶片效果采用强调色
else:
t.pencolor("#30363C") # 树干采用主色
# 绘制主干
t.forward(branch_len)
# 递归绘制左侧子树
t.left(angle)
draw_tree(t, branch_len * ratio, angle, ratio, order - 1)
# 转向右侧并绘制右侧子树
t.right(2 * angle)
draw_tree(t, branch_len * ratio, angle, ratio, order - 1)
# 状态回溯:恢复初始角度并倒退回起始位置
t.left(angle)
t.penup()
t.backward(branch_len)
t.pendown()
def main():
screen = turtle.Screen()
screen.setup(width=800, height=750)
screen.bgcolor("#F8F7F3")
screen.tracer(0, 0)
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
# 将海龟定位至底部中央,朝向正上方
t.penup()
t.goto(0, -280)
t.left(90)
t.pendown()
draw_tree(t, branch_len=110, angle=25, ratio=0.76, order=9)
screen.update()
screen.mainloop()
if __name__ == "__main__":
main()
四、实例三:谢尔宾斯基三角形(Sierpinski Triangle)
谢尔宾斯基三角形是一种典型的空间填充型分形。它的构造方式是将一个等边三角形内部连接三条边的中点,抠掉中间倒置的中心三角形,然后在剩下的三个较小正等边三角形中重复上述操作。
在代码实现中,直接使用三顶点的笛卡尔坐标运算 midpoint = ((x1+x2)/2, (y1+y2)/2),比控制海龟转角和步长更直接且数值误差更小:
import turtle
def get_midpoint(p1, p2):
"""计算两点中点坐标"""
return ((p1[0] + p2[0]) / 2.0, (p1[1] + p2[1]) / 2.0)
def draw_filled_triangle(t, points, color):
"""绘制填充单色三角形"""
t.fillcolor(color)
t.penup()
t.goto(points[0])
t.pendown()
t.begin_fill()
t.goto(points[1])
t.goto(points[2])
t.goto(points[0])
t.end_fill()
def sierpinski(t, points, order):
"""
递归绘制谢尔宾斯基三角形
:param points: 三个顶点的坐标元组列表 [(x1,y1), (x2,y2), (x3,y3)]
:param order: 递归阶数
"""
if order == 0:
draw_filled_triangle(t, points, "#30363C")
else:
# 获取三边中点
mid_01 = get_midpoint(points[0], points[1])
mid_12 = get_midpoint(points[1], points[2])
mid_20 = get_midpoint(points[2], points[0])
# 在三个角上的子三角形继续递归
sierpinski(t, [points[0], mid_01, mid_20], order - 1)
sierpinski(t, [points[1], mid_01, mid_12], order - 1)
sierpinski(t, [points[2], mid_12, mid_20], order - 1)
def main():
screen = turtle.Screen()
screen.setup(width=800, height=750)
screen.bgcolor("#F8F7F3")
screen.tracer(0, 0)
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
# 初始等边三角形顶点
vertices = [(-260, -180), (0, 240), (260, -180)]
sierpinski(t, vertices, order=5)
screen.update()
screen.mainloop()
if __name__ == "__main__":
main()
五、性能调优与避坑注意事项
重要提示:递归几何图形的分支数量呈指数级递增。例如科赫雪花在深度为 $N$ 时,小线段数量达到 $3 \times 4^N$;若将递归阶数随意设为 7 以上,线段数将激增至数万条,极易导致窗口假死或耗尽 Python 栈深度。
在实际编写与调试分形代码时,建议严格注意以下三项控制要点:
- 关闭实时动画渲染:Turtle 默认会在绘制每一段时重绘画布。必须在循环或递归前调用
screen.tracer(0, 0)禁用自动绘制,并在绘制结束后显式调用screen.update()一次性渲染,渲染速度可提升 100 倍以上。 - 控制合理递归深度:对于二叉树或谢尔宾斯基三角形,通常选择
5 ~ 9阶即可获得细腻的视觉效果。Python 默认递归深度上限为 1000(可通过sys.setrecursionlimit()调整,但在分形几何绘制中,通常更早触及的是渲染节点过多的性能瓶颈而非栈深度)。 - 避免浮点累计误差:长时间使用
forward()与left()变换时,正弦/余弦浮点运算在深层递归中会累积细微偏差,导致最终闭合图形产生肉眼可见的裂口。在可能的情况下,优先使用绝对坐标中点计算(如实例三)。
六、总结
分形艺术的迷人之处,在于它用最简短的代码展现出了无限嵌套的秩序之美。在 Python 中利用 turtle 实现分形的核心脉络非常清晰:首先明确自相似规则,然后设定严格的基线终止条件,在递归深入时缩小尺度,在递归归来时严格重置位姿。结合 screen.tracer(0, 0) 这一关键性能开关,你便能自如调整角度、枝干衰减比以及配色方案,创造出属于自己的几何视觉作品。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









