希尔伯特曲线,也被称作希尔伯特空间填充曲线,是一个连续分形曲线。它由德国数学家大卫·希尔伯特在1891年提出,旨在证明可以通过连续运动完全填满一个平面区域。
希尔伯特曲线是通过迭代过程构造的。在每一次迭代中,曲线变得更加复杂,并更紧密地逼近所包围的二维区域。这种曲线作为一种分形结构,其不仅呈现出重复性和自相似性,而且具有无限长的长度,却在有限的区域内。
认识希尔伯特曲线
从左到右,从上到下分别为1-4阶希尔伯特曲线
希尔伯特曲线的构造
希尔伯特曲线的构造方法是一个递归过程,要构造n阶的希尔伯特曲线,先构造4个n-1阶的希尔伯
特曲线,这4个n-1阶的希尔伯特曲线通过特定的顺序连接起来。
以图像中央为原点,划分象限
观察发现:第一象限图形为前一阶数图形绕中心逆时针旋转90度并做轴对称所得
第三、四象限图形为原图形
第二象限图形为前一阶数图形绕中心顺时针旋转90度并做轴对称所得
且每一象限之间通过单位长度的线段,将上一象限图形的终点与下一象限图形的起点连接
希尔伯特曲线的每一级迭代通常可以用以下步骤描述:
第0阶: 从一个点或者一个简单的线段开始,作为迭代的基础。
第1阶: 将现有的线段分为四份,然后按特定的顺序和方向通过这些点构建一个环形的路径。
更高阶: 对于每一阶迭代,都取前一阶曲线,按照规则复制四份,然后进行旋转和连接,形成新的一阶曲线。
经过资料查找与归纳总结,得到如下的规律
注:指令字符意义
# '-' 代表左转90度
# '+' 代表右转90度
# 'f' 代表向前画线(沿着当前方向)
hilbert_map = { 'a': "-bf+afa+fb-", # 若当前部分曲线是‘a’类型,则按照这串字符进行操作和递归生成 'b': "+af-bfb-fa+", # 若当前部分曲线是‘b’类型,则按照这串字符进行操作和递归生成 } def hilbert(t, char, angle, length, depth): if depth > 0: # 当递归深度大于0时,进行递归绘制 instructions = hilbert_map[char] # 获取当前部分曲线类型的操作指令 for cmd in instructions: # 遍历指令字符,执行相应操作 if cmd == 'a' or cmd == 'b': # 如果遇到部分曲线类型,进行更深一层递归 hilbert(t, cmd, angle, length, depth - 1) elif cmd == '-': # 如果遇到'-'字符,乌龟左转90度 t.left(angle) elif cmd == '+': # 如果遇到'+'字符,乌龟右转90度 t.right(angle) elif cmd == 'f': # 如果遇到'f'字符,乌龟向前移动指定长度并画线 t.forward(length)
讯享网
其中
# t:turtle对象,执行绘图的实体
# char:当前递归所处理的部分曲线类型(‘a’或者‘b’)
# angle:转动角度,希尔伯特曲线使用的是90度
# length:每向前移动画线时的长度
# depth:阶数
解释一下:+ 和 - 是转向命令,f 表示画线命令。a和b为递归展开部分,在本级曲线不做实际画图操作。
举例解释:
首先定义规则:
hilbert_map = {
'a': "-bf+afa+fb-",
'b': "+af-bfb-fa+",
}
第0阶希尔伯特曲线:
第0阶可以简单地表示为单个起始字符 b。
第1阶希尔伯特曲线:
以 b 作为起始字符,它按照 hilbert_map 中定义的替换规则 "+af-bfb-fa+" 被替换,生成第1阶曲线。
第2阶希尔伯特曲线:

为了进行到第2阶曲线的生成,我们将对上一阶曲线(即现在的第1阶)中每个字符按照替换规则进行扩展。这次我们需要对包含 a 和 b 的每一部分进行替换。每一个 a 和 b 将根据 hilbert_map 中的规则被展开,整个 b 的替换过程如下:
+:表示右转90度;
a:替换为第1阶中的 a 对应的操作序列 -bf+afa+fb-;
f:表示向前画线;
-:表示左转90度;
b:替换为第1阶中的 b 对应的操作序列 +af-bfb-fa+,同理,它将被进一步替换;
f:表示向前画线;
b:被扩展替换;
-:表示左转90度;
f:表示向前画线;
a:被扩展替换;
+:表示右转90度;
对于 a 和 b 的每个实例,你会按照相同的步骤继续递归替换,直到达到2阶曲线的完整结构。
可以理解成对于a,b进行对应扩展,扩展指定次数(根据曲线阶数而定)之后,按照表达式顺序再一起执行
主要步骤:扩展(递归替换)和执行(绘制)。
- 扩展(Replacement or Expansion) 这一步是递归的。它遵循提前定义好的规则(提供的
hilbert_map字典),将当前阶段的每个字符替换为对应更复杂的序列,以生成下一阶段的曲线表达式。这一步不涉及实际的图形绘制,而只是按照规则生成更长的字符串序列。 - 执行(Execution) 扩展完成之后,获得的是一长串的字符序列,每个字符代表一个操作指令。这时再根据这个序列逐个执行字符对应的动作来进行实际的绘制,包括前进(
f)、左转(-)和右转(+)的动作。这个步骤实际上构建和描绘了曲线的路径
模拟从’b‘开始的多次扩展过程:
b
=>
+af-bfb-fa+
=>
=>
...
后面的画图基础操作不做详细说明,仅附上相应代码与注释
讯享网import turtle # 指令字符意义: # '-' 代表左转90度 # '+' 代表右转90度 # 'f' 代表向前画线(沿着当前方向) # 'b' 代表向后画线(沿着当前方向的反方向) # 注:在本示例中,'b' 实际上未被使用 # 希尔伯特曲线的迭代规则定义 # 字典中的键’a’和‘b’表示特定的部分曲线类型 # 字典中的值表示相应部分曲线类型的生成规则 hilbert_map = { 'a': "-bf+afa+fb-", # 若当前部分曲线是‘a’类型,则按照这串字符进行操作和递归生成 'b': "+af-bfb-fa+", # 若当前部分曲线是‘b’类型,则按照这串字符进行操作和递归生成 } # 递归函数定义:绘制希尔伯特曲线 # t:turtle对象,执行绘图的实体 # char:当前递归所处理的部分曲线类型(‘a’或者‘b’) # angle:转动角度,希尔伯特曲线使用的是90度 # length:每向前移动画线时的长度 # depth:阶数 def hilbert(t, char, angle, length, depth): if depth > 0: # 当递归深度大于0时,进行递归绘制 instructions = hilbert_map[char] # 获取当前部分曲线类型的操作指令 for cmd in instructions: # 遍历指令字符,执行相应操作 if cmd == 'a' or cmd == 'b': # 如果遇到部分曲线类型,进行更深一层递归 hilbert(t, cmd, angle, length, depth - 1) elif cmd == '-': # 如果遇到'-'字符,乌龟左转90度 t.left(angle) elif cmd == '+': # 如果遇到'+'字符,乌龟右转90度 t.right(angle) elif cmd == 'f': # 如果遇到'f'字符,乌龟向前移动指定长度并画线 t.forward(length) # 初始化turtle窗口 window = turtle.Screen() # 创建绘图窗口对象 window.bgcolor("white") # 设置窗口背景颜色为白色 # 创建一个turtle对象,也即"乌龟",用于执行绘制希尔伯特曲线的操作 tur = turtle.Turtle() # 创建一个turtle实例 tur.speed(0) # 设置绘图速度,0为最快 tur.hideturtle() # 绘图过程中隐藏乌龟图标 tur.penup() # 抬起画笔,移动到起始绘制位置时不留下痕迹 # 设置乌龟的起始位置,位于屏幕左上四分之一处 tur.setpos(-window.window_width()//4, window.window_height()//4) tur.pendown() # 放下画笔准备开始绘图 # 现在来设定绘制希尔伯特曲线的几个主要参数 # 希尔伯特曲线的阶数,这里是4阶曲线, # 即按照给定的'a'或'b'类型的基础规则进行一次曲线生成过程 depth = 4 # 绘图时乌龟每次移动画线的长度 length = 25 # 转弯的角度,对于希尔伯特曲线是90度 angle = 90 # 调用hilbert函数开始绘制,其开始的部分曲线类型为'b' # 因为希尔伯特曲线是递归定义的,选择'a'或'b'作为起始类型将产生不同的初始模式 hilbert(tur, 'b', angle, length, depth) # 绘图完成后隐藏乌龟图标,以便清晰地查看绘制结果 tur.hideturtle() # 保持绘图窗口开启,直到用户关闭,可在窗口中欣赏绘制出的希尔伯特曲线 window.exitonclick()
本文为自己学习时所写,若有错误,敬请批评指正!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/126333.html