科赫雪花
科赫雪花于1904年由瑞士数学家黑格尔-冯-科赫提出,是一种分形,即一种不断放大时会重复自己的图形。分形源自递归,递归操作本身就是在重复使用自己定义自己。其图片如下:

递归的基础知识
递归操作的基本步骤
step1:定义一个基线条件,达到条件时,自动结束递归
step2:写一个算法可能用到多次递归
阶层的表达式就是一个经典的递归,
python
def factorial(N):
if N == 1
return 1
else:
return N*factorial(N-1)
循环与递归的选择方面:各有千秋,递归优雅紧凑,循环高效快捷,本节我们将用递归绘制分形。
数学内容补充
垂直向量计算技巧:已知点A(a,b),当点 B为B (-b,a) 时,
从空间中的一点A沿着向量n出发,到达点B的坐标运算公式:
已知线段的两个端点A和B,求其上面任意一点C的坐标公式, 其中a为点A到点C的距离,b为点B到点C的距离
科赫雪花的相关参数计算

这是科赫雪花的第一个片段,我们一共需要3个相连接的片段。





海龟turtle绘图
绘制三角形--热身
t.up()/t.down():海龟绘图的核心操作,抬笔移动不画线,落笔移动才画线,这里用来 "跳" 到指定坐标,避免画多余的线;setpos(x,y):等价于goto(x,y),让画笔移动到指定坐标,落笔时会画直线;
triangle.py
python
import turtle
def draw_triangle(x1, y1, x2, y2, x3, y3,t):
t.up()
t.setpos(x1, y1)
t.down()
t.setpos(x2, y2)
t.setpos(x3, y3)
t.setpos(x1, y1)
t.up()
def main():
print('testing turtle graphics...')
# 创建海龟对象
t = turtle.Turtle()
# 隐藏海龟图标
t.hideturtle()
# 调用函数,传入海龟对象和坐标,确保绘制完成后不会关闭tkinter窗口
draw_triangle(t, -100, 0, 0, 173.2, 100, 0)
turtle.mainloop()
if __name__ == "__main__":
main()

绘制科赫雪花--实践
第一步:把原始大线段 (x1,y1)→(x2,y2) 拆成 4 段更小的线段(科赫雪花的规则):
- 第 1 段:
(x1,y1) → p1(三等分第一个点) - 第 2 段:
p1 → p2(等边三角形的第一条边) - 第 3 段:
p2 → p3(等边三角形的第二条边) - 第 4 段:
p3 → (x2,y2)(三等分第二个点)
第二步:对这 4 条小线段 ,重复调用同一个函数(递归),让每条小线段也执行 "拆分成 4 段 / 直接画" 的逻辑 ------ 所以形参变成了每条小线段的 "起点 + 终点"。
koch.py
python
import turtle
import math
# drawKochSF()计算点的坐标
def drawKochSF(x1, y1, x2, y2, t):
d = math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
r = d/3.0
h = r*math.sqrt(3)/2.0
p3 = ((x1 + 2*x2)/3.0, (y1 + 2*y2)/3.0)
p1 = ((2*x1 + x2)/3.0, (2*y1 + y2)/3.0)
c = (0.5*(x1+x2), 0.5*(y1+y2))
n = ((y1-y2)/d, (x2-x1)/d)
p2 = (c[0]+h*n[0], c[1]+h*n[1])
# 递归
if d > 10:
# flake #1/片段
drawKochSF(x1, y1, p1[0], p1[1], t)
# flake #2/片段
drawKochSF(p1[0], p1[1], p2[0], p2[1], t)
# flake #3/片段
drawKochSF(p2[0], p2[1], p3[0], p3[1], t)
# flake #4/片段
drawKochSF(p3[0], p3[1], x2, y2, t)
else:
# draw cone
t.up()
t.setpos(p1[0], p1[1])
t.down()
t.setpos(p2[0], p2[1])
t.setpos(p3[0], p3[1])
# draw sides
t.up()
t.setpos(x1, y1)
t.down()
t.setpos(p1[0], p1[1])
t.up()
t.setpos(p3[0], p3[1])
t.down()
t.setpos(x2, y2)
# main() function
def main():
print('Drawing the Koch Snowflake...')
t = turtle.Turtle()
t.hideturtle()
# draw
try:
drawKochSF(-100, 0, 100, 0, t)
drawKochSF(0, -173.2, -100, 0, t)
drawKochSF(100, 0, 0, -173.2, t)
except:
print("Exception, exiting.")
exit(0)
# wait for user to click on screen to exit
turtle.Screen().exitonclick()
# call main
if __name__ == '__main__':
main()
try-except 代码块**,异常捕获机制**,专门用来处理绘制过程中可能出现的错误,避免程序直接崩溃。

- 递归阈值:线段长度
d > 10时递归拆分,d ≤ 10时执行else里的绘制逻辑; - 雪花由 3 条线段组成:
(-100,0)→(100,0)、(0,-173.2)→(-100,0)、(100,0)→(0,-173.2);
阶段 1:程序启动与初始化
python
if __name__ == '__main__':
main() # 调用主函数
- 执行
main()函数,首先打印提示:Drawing the Koch Snowflake...; - 创建海龟画笔对象:
t = turtle.Turtle(),隐藏画笔箭头:t.hideturtle()(画笔默认速度,无加速); - 进入
try代码块,准备依次绘制 3 条科赫边。
阶段 2:绘制第一条边 drawKochSF(-100, 0, 100, 0, t)
步骤 1:计算几何参数(第一次调用)
- 线段长度
d = √[(-100-100)² + (0-0)²] = 200(>10,触发递归); - 三等分长度
r = 200/3 ≈ 66.67,等边三角形高h = 66.67×√3/2 ≈ 57.74; - 三等分点:
p1 = (2×(-100)+100)/3, (2×0+0)/3 = (-33.33, 0);p3 = (-100+2×100)/3, (0+2×0)/3 = (33.33, 0); - 线段中点
c = (0, 0); - 法向量
n = (0-0)/200, (100-(-100))/200 = (0, 1); - 凸起顶点
p2 = (0 + 57.74×0, 0 + 57.74×1) = (0, 57.74)。
步骤 2:递归拆分(d=200>10)
函数不绘制,而是拆成 4 段,依次递归调用自身:
- 调用 1:
drawKochSF(-100, 0, -33.33, 0, t)→ 处理第一段(起点→p1); - 调用 2:
drawKochSF(-33.33, 0, 0, 57.74, t)→ 处理第二段(p1→p2); - 调用 3:
drawKochSF(0, 57.74, 33.33, 0, t)→ 处理第三段(p2→p3); - 调用 4:
drawKochSF(33.33, 0, 100, 0, t)→ 处理第四段(p3→终点)。
步骤 3:递归深入(直到 d ≤ 10)
以「调用 1:drawKochSF(-100, 0, -33.33, 0, t)」为例,继续拆解:
- 计算该线段长度
d = √[(-100+33.33)² + 0] ≈ 66.67(>10,继续拆); - 计算该段的
p1/p2/p3(比如新 p1≈-77.78,新 p3≈-55.56,新 p2≈-66.67, 19.25); - 再次拆成 4 段,递归调用......
- 这个过程会持续,直到某次计算出的
d ≤ 10(比如拆到第 3 层,d≈7.41),触发else分支。
python
else:
# 1. 绘制"凸起":p1→p2→p3
t.up()
t.setpos(p1[0], p1[1]) # 移动到当前短线段的p1
t.down()
t.setpos(p2[0], p2[1]) # 画p1→p2
t.setpos(p3[0], p3[1]) # 画p2→p3
# 2. 绘制两端:起点→p1,p3→终点
t.up()
t.setpos(x1, y1) # 移动到当前短线段的起点
t.down()
t.setpos(p1[0], p1[1]) # 画起点→p1
t.up()
t.setpos(p3[0], p3[1]) # 移动到当前短线段的p3
t.down()
t.setpos(x2, y2) # 画p3→终点
步骤 5:递归回溯
完成当前短线段的绘制后,函数执行完毕,回到上一层递归调用,继续处理下一段,直到第一层递归的 4 段都处理完,第一条边绘制结束。
阶段 3:绘制第二、第三条边(重复阶段 2)
- 调用
drawKochSF(0, -173.2, -100, 0, t):- 计算该线段长度≈200(>10),递归拆分→直到 d≤10,执行你的
else绘制逻辑; - 这条边是「(0,-173.2)→(-100,0)」,是倒置等边三角形的左斜边;
- 计算该线段长度≈200(>10),递归拆分→直到 d≤10,执行你的
- 调用
drawKochSF(100, 0, 0, -173.2, t):- 同理,递归拆分→绘制,是倒置等边三角形的右斜边。
阶段 4:程序收尾
- 3 条边的递归绘制全部完成(无论图形是否正常);
- 执行
turtle.Screen().exitonclick():等待你点击画布,程序退出; - 若绘制中出现任何错误(比如递归深度超限、坐标计算异常),触发
except块:打印Exception, exiting.并退出程序。