科赫雪花--Python--数学原理--turtle绘图

科赫雪花

科赫雪花于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()  # 调用主函数
  1. 执行 main() 函数,首先打印提示:Drawing the Koch Snowflake...
  2. 创建海龟画笔对象:t = turtle.Turtle(),隐藏画笔箭头:t.hideturtle()(画笔默认速度,无加速);
  3. 进入 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)
  1. 调用 drawKochSF(0, -173.2, -100, 0, t)
    • 计算该线段长度≈200(>10),递归拆分→直到 d≤10,执行你的 else 绘制逻辑;
    • 这条边是「(0,-173.2)→(-100,0)」,是倒置等边三角形的左斜边;
  2. 调用 drawKochSF(100, 0, 0, -173.2, t)
    • 同理,递归拆分→绘制,是倒置等边三角形的右斜边。
阶段 4:程序收尾
  1. 3 条边的递归绘制全部完成(无论图形是否正常);
  2. 执行 turtle.Screen().exitonclick():等待你点击画布,程序退出;
  3. 若绘制中出现任何错误(比如递归深度超限、坐标计算异常),触发 except 块:打印 Exception, exiting. 并退出程序。
相关推荐
宝贝儿好2 小时前
【强化学习】第十章:连续动作空间强化学习:随机高斯策略、DPG算法
人工智能·python·深度学习·算法·机器人
scott1985123 小时前
Improving Classifier-Free Guidance of Flow Matching via Manifold Projection
人工智能·python·机器学习
iOS开发上架3 小时前
系统架构-信息系统
python·腾讯云
Lupino3 小时前
从逻辑“脑裂”到 AI 重构:不到 2 美金解决物联网电位反转难题
python·物联网
青春不败 177-3266-05203 小时前
最新AI赋能Python长时序植被遥感动态分析、物候提取、时空变异归因及RSEI生态评估
人工智能·python·生态学·植被遥感·遥感·物候提取
NGINX开源社区3 小时前
使用 Microsoft Entra ID 配置 NGINX Plus 以实现 SAML SSO
后端·python·flask
小鸡吃米…3 小时前
基于 TensorFlow 的图像识别
人工智能·python·tensorflow
小鸡吃米…4 小时前
TensorFlow - 构建计算图
人工智能·python·tensorflow
cipher4 小时前
crawl4ai:AI时代的数据采集利器——从入门到实战
后端·爬虫·python