哈Ha!今天关于分形的文章是作为Python主题(尤其是Matplotlib)的一部分出现的。遵循作者的示例,并警告说,帖子中有很多沉重的动画甚至可能无法在移动设备上运行。但是,多么美丽。
所有人都喜欢阅读分形很漂亮。它们按照非常复杂的图案排列,并且在任何放大倍率下都不会变形!在本文中,我们将研究如何使用称为L-Systems的工具和Python的Turtle模块轻松地绘制几种分形,以逐步进行绘制。在本文中,我们不会过多地讨论技术细节;相反,我仅作简要介绍,展示许多动画示例和代码,您可以使用这些示例和代码生成这样的示例。如果您想跳过理论而只看动画,请直接转到示例。此外,最后,我将指出您可能想探索的一些资源,包括代码编写和基础数学。什么是分形?首先,让我们给出一个分形的“松散”定义。原则上,分形是一个几何图形,无论增加的程度如何,它都具有相同的特性。这个定义并不完美,因此以下是Math World网站提供的更准确的定义:分形是在任何尺度上都表现出自相似性的对象或数量。一个物体在不同的尺度上显示出不同的结构,但是相同的“类型”结构必须出现在分形的所有水平上。在这种情况下,将图形绘制在具有对数刻度的坐标系中,其中沿轴计算幅度和比例,然后图形是一条直线,其斜率反映了分形的维数。- 数学世界
如何使用Python绘制分形?通常,渲染分形很复杂,因为分形的深层性质由递归概念决定。在谈到图形及其绘制时,我们通常认为它们是由像素或矢量形成的,但是像素或矢量的数量始终受到限制,分形从定义上说是无限递归的。因此,尝试将分形应用于坐标网格,我们将不得不在某个点处停止,这就是为什么在这种情况下我们谈论“迭代”的原因。在每次迭代中,分形变得更加复杂,并且在某个点上变得无法区分彼此相邻的两次迭代(这种情况发生在当变化发生在与像素大小相当的水平时)。在这里停下来是合乎逻辑的,但是通常,分形的形状会更快出现,甚至可以更早停下来。两个这样的示例是Koch的方形岛,它的结构在经过3次迭代后就清楚地显现出来;还有Carter-Heitway龙,对于它们,经过8次迭代就可以构建完整的结构。所需的迭代次数高度取决于我们正在使用的特定分形。当然,有许多用于绘制的Python库,其中最受欢迎的是Matplotlib,但是它们通常是为绘制统计数据和绘制知名图形而设计的。尤其是Matplotlib包含一些可用于构造分形的低级构造,但是这次我们将集中讨论标准库称为Turtle的鲜为人知的模块。龟模块在Python文档我们读到:“对于初次接触编程的孩子,海龟图形是一种流行的工具。他是由WallyFörzeg和Seymour Papert在1966年开发的原始徽标编程语言的一部分。”最重要的是,默认情况下,乌龟可以识别3个命令:注意:标准库中提供了其他命令,但是在这里我们仅使用这三个命令。我们还可以:这些特征似乎太简单,无法仅依靠它们来绘制像分形这样的复杂图形,但是我们将使用另一种仅使用这一小部分指令的工具。我说的是L系统。L系统L系统是一种将递归结构(例如,分形)表示为字符串,并多次重写此字符串的方法。同样,我们给出一个正式定义:Lindenmeyer系统,也称为L系统,是一种线重写机制,可用于生成尺寸从1到2的分形-Math World
了解了L系统是什么之后,我们可以创建递归结构,但首先让我们弄清楚为此需要哪些组件。每个L系统具有:致计算机科学爱好者的注意事项:如果您已深入学习计算机科学,那么以上所有内容可能会让您想起某些事情。确实,形式语法的定义非常相似。关键区别在于,与语法不同,此处每次迭代都应用尽可能多的规则,而不仅仅是一个。因此,L系统是上下文无关文法的子集。假定我们要使用Turtle来构建图形,并使用L系统来表示要绘制的内容,我们需要在它们之间创建关系。由于在Turtle中,我们只有上面列出的团队,因此我们为每个团队分配一个符号;字母将由这些字符组成。为此,必须为每个分形提供一个角度。这将是乌龟向右或向左旋转的角度。为简单起见,我们同意只应提供一个角落,因此我们将牢记这一点编写L系统。创建字符串的公理和指令将仅取决于分形,但是分形必须以只能由这三个字符表示的方式编写。因此存在一个限制,据此我们只能建立单线分形,即无法以这种方式获得类似Cantor集的东西。但这只是一个简化,因为我们总是可以输入另外两个命令来前进而不记录,而对于后退同样可以输入。现在让我们来看例子!动画示例以下示例是从几个可公开获得的资源在线获得的,我决定使用Turtle模块将它们移植到Python,对其进行居中,着色以及提供一种导出为矢量格式的方法。注意:建议的动画很大,建议仅在良好的互联网环境下观看。 Repl代码可能不起作用,因为它会占用您的资源,并且在移动设备上显示分形可能会出现问题。注意:Skulpt使用您的浏览器来渲染和创建动画,因此当您挂起,滞后或任何奇怪的行为时,通常只需重播动画或重新加载页面就足够了。可能不适用于移动设备。这些示例是按照复杂性顺序给出的(根据我的主观观点),因此最有趣的是最后。科赫雪花axiom = "F--F--F"
rules = {"F":"F+F--F+F"}
iterations = 4
angle = 60
科克广场岛axiom = "F+F+F+F"
rules = {"F":"F-F+F+FFF-F-F+F"}
iterations = 2
angle = 90
水晶axiom = "F+F+F+F"
rules = {"F":"FF+F++F+F"}
iterations = 3
angle = 90
方形雪花axiom = "F--F"
rules = {"F":"F-F+F+F-F"}
iterations = 4
angle = 90
分形维切卡axiom = "F-F-F-F"
rules = {"F":"F-F+F+F-F"}
iterations = 4
angle = 90
征税曲线axiom = "F"
rules = {"F":"+F--F+"}
iterations = 10
angle = 45
谢尔宾斯基地毯axiom = "YF"
rules = {"X":"YF+XF+Y", "Y":"XF-YF-X"}
iterations = 1
angle = 60
谢尔宾斯基格子axiom = "FXF--FF--FF"
rules = {"F":"FF", "X":"--FXF++FXF++FXF--"}
iterations = 7
angle = 60
广场axiom = "F+F+F+F"
rules = {"F":"FF+F+F+F+FF"}
iterations = 3
angle = 90
瓷砖axiom = "F+F+F+F"
rules = {"F":"FF+F-F+F+FF"}
iterations = 3
angle = 90
戒指axiom = "F+F+F+F"
rules = {"F":"FF+F+F+F+F+F-F"}
iterations = 2
angle = 90
十字架2axiom = "F+F+F+F"
rules = {"F":"F+F-F+F+F"}
iterations = 3
angle = 90
五重性axiom = "F++F++F++F++F"
rules = {"F":"F++F++F+++++F-F++F"}
iterations = 1
angle = 36
32段曲线axiom = "F+F+F+F"
rules = {"F":"-F+F-F-F+F+FF-F+F+FF+F-F-FF+FF-FF+F+F-FF-F-F+FF-F-F+F+F-F+"}
iterations = 3
angle = 90
皮亚诺·戈斯珀曲线axiom = "FX"
rules = {"X":"X+YF++YF-FX--FXFX-YF+", "Y":"-FX+YFYF++YF+FX--FX-Y"}
iterations = 4
angle = 60
谢尔宾斯基曲线axiom = "F+XF+F+XF"
rules = {"X":"XF-F+F-XF+F+XF-F+F-X"}
iterations = 4
angle = 90
克里希纳的小问题axiom = " -X--X"
rules = {"X":"XFX--XFX"}
iterations = 3
angle = 45
戈斯珀广场分形axiom = "YF"
rules = {"X": "XFX-YF-YF+FX+FX-YF-YFFX+YF+FXFXYF-FX+YF+FXFX+YF-FXYF-YF-FX+FX+YFYF-",
"Y": "+FXFX-YF-YF+FX+FXYF+FX-YFYF-FX-YF+FXYFYF-FX-YFFX+FX+YF-YF-FX+FX+YFY"}
iterations = 2
angle = 90
摩尔曲线axiom = "LFL-F-LFL"
rules = {"L":"+RF-LFL-FR+", "R":"-LF+RFR+FL-"}
iterations = 0
angle = 90
希尔伯特曲线axiom = "L"
rules = {"L":"+RF-LFL-FR+", "R":"-LF+RFR+FL-"}
iterations = 8
angle = 90
希尔伯特曲线IIaxiom = "X"
rules = {"X":"XFYFX+F+YFXFY-F-XFYFX", "Y":"YFXFY-F-XFYFX+F+YFXFY"}
iterations = 4
angle = 90
豌豆曲线axiom = "F"
rules = {"F":"F+F-F-F-F+F+F+F-F"}
iterations = 2
angle = 90
交叉axiom = "F+F+F+F"
rules = {"F":"F+FF++F+F"}
iterations = 3
angle = 90
三角形axiom = "F+F+F"
rules = {"F":"F-F+F"}
iterations = 2
angle = 120
龙曲线axiom = "FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 8
angle = 90
Terdragon曲线axiom = "F"
rules = {"F":"F-F+F"}
iterations = 5
angle = 120
双龙曲线axiom = "FX+FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 6
angle = 90
三重龙曲线axiom = "FX+FX+FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 7
angle = 90
代码以上所有示例都是使用相同的代码获得的,在使用它们时,存在一些困难(例如,如何使分形尽可能保持在中心),使用颜色,反转,偏移量以及快速导出到矢量格式。在这里,我仅向您展示最简单的版本。此版本以黑白显示分形,并且不具备导出功能import turtle
def create_l_system(iters, axiom, rules):
start_string = axiom
if iters == 0:
return axiom
end_string = ""
for _ in range(iters):
end_string = "".join(rules[i] if i in rules else i for i in start_string)
start_string = end_string
return end_string
def draw_l_system(t, instructions, angle, distance):
for cmd in instructions:
if cmd == 'F':
t.forward(distance)
elif cmd == '+':
t.right(angle)
elif cmd == '-':
t.left(angle)
def main(iterations, axiom, rules, angle, length=8, size=2, y_offset=0,
x_offset=0, offset_angle=0, width=450, height=450):
inst = create_l_system(iterations, axiom, rules)
t = turtle.Turtle()
wn = turtle.Screen()
wn.setup(width, height)
t.up()
t.backward(-x_offset)
t.left(90)
t.backward(-y_offset)
t.left(offset_angle)
t.down()
t.speed(0)
t.pensize(size)
draw_l_system(t, inst, angle, length)
t.hideturtle()
wn.exitonclick()
代码说明import turtle
首先,您需要导入Turtle模块def create_l_system(iters, axiom, rules):
start_string = axiom
if iters == 0:
return axiom
end_string = ""
for _ in range(iters):
end_string = "".join(rules[i] if i in rules else i for i in start_string)
start_string = end_string
return end_string
然后,您需要生成一个L系统,这将是乌龟的一组指令。我们定义了一个函数create_l_system
,该函数接收迭代次数,公理和构造规则。它以公理开始,并使用辅助变量end_string
,如果迭代次数为0,则它将返回公理,因为某些分形也可以应用零迭代。在这种情况下,假定规则具有字典的形式,因此每个键都是唯一的,代表一个符号,并且值指示需要替换的内容。因此,我们将每个字符的所有替换组合在一起,并最终为下一次迭代获取一个字符串。def draw_l_system(t, instructions, angle, distance):
for cmd in instructions:
if cmd == 'F':
t.forward(distance)
elif cmd == '+':
t.right(angle)
elif cmd == '-':
t.left(angle)
然后,我们确定draw_l_system
哪个接受乌龟,一组指令(L系统的输出),左转或右转的角度以及每条线的长度。它由elif
每个先前定义的团队的简单结构组成。def main(iterations, axiom, rules, angle, length=8, size=2, y_offset=0,
x_offset=0, offset_angle=0, width=450, height=450):
inst = create_l_system(iterations, axiom, rules)
t = turtle.Turtle()
wn = turtle.Screen()
wn.setup(width, height)
t.up()
t.backward(-x_offset)
t.left(90)
t.backward(-y_offset)
t.left(offset_angle)
t.down()
t.speed(0)
t.pensize(size)
draw_l_system(t, inst, angle, length)
t.hideturtle()
wn.exitonclick()
最后,让我们说说功能main
,它采取一切必要的L-系统的生成参数,以及y_offset
,x_offset
,offset_angle
,width
和height
。前三个描述了乌龟的位移,只需要按照我们想要的方式将图形放置在画布上即可。该函数首先生成一组指令并将其保存在inst中,然后初始化乌龟和屏幕并将乌龟放置在特定点,然后根据指令绘制图形并等待单击以关闭。特别注意事项正如我上面提到的,这里还有许多限制。首先,我们没有为乌龟提供无需渲染即可移动的能力;这将需要另一个字符。也没有用于退回和记住先前位置的符号。它们不是上面讨论的所有分形所必需的,但是其他一些分形(例如,分形树)是必需的。其他资源互联网上有很多关于分形的资源,无论是从编程的角度还是从数学的角度来看,分形的资源都被考虑在内。以下两个对我来说似乎特别有趣:3Blue1Brown(数学)和CodingTrain(代码)。这篇文章是由灵感后从数学世界和文章 宝拉·伯卡(Paula Burka)。