我已经编写了一些代码,这些代码以一种非常低效的方式来表示Mandelbrot集。这看上去可能很糟糕,因为我完全是独自完成的,除了wiki页面之外,没有任何外部来源。我想知道的是,如果不完全重写代码,我如何才能修复代码,使其运行得更快,或者是更高的定义?
最后图像:

import turtle
x = -200
y = 99
RN = -2
i = float(1.0)
c = complex(RN+i)
win = turtle.Screen()
win.setup(400,400)
t = turtle.Turtle()
t.speed(0)
t.penup()
t.ht()
pattern = []
#this calculates what the color hex value each pixel will be. if the 10th
#iteration results in a number greater than 2, then that pixel is outside
#of the set.
def set_color(RN,pattern,z,c):
for v in range(10):
z = z**2 + c
pattern.append(z)
if abs(z) > 3.7918623e+90:
t.color("#191970")
elif abs(z) > 1.9472705e+45:
t.color("#16167F")
elif abs(z) > 4.4127888e+22:
t.color("#13138F")
elif abs(z) > 210066388901:
t.color("#10109F")
elif abs(z) > 458330:
t.color("#0D0DAF")
elif abs(z) > 677:
t.color("#0B0BBF")
elif abs(z) > 26:
t.color("#0808CF")
elif abs(z) > 5:
t.color("#0505DF")
elif abs(z) > 2:
t.color("#0202EF")
elif abs(z) > 1.75:
t.color("yellow")
elif abs(z) > 1.5:
t.color("gold")
elif abs(z) > 1.25:
t.color("goldenrod")
elif abs(z) > 1:
t.color("darkgoldenrod")
else:
t.color("black")
#draws the set
def Mandelbrot(RN,pattern,precision,check_rate,move_rate,x,y,i,c):
#draws line by line, 100 times
for e in range(100):
t.pendown()
#draws one line
for e in range(precision):
z = 0
set_color(RN,pattern,z,c)
pattern = []
RN = float(RN + move_rate)
c = complex(RN,i)
x = x + check_rate
t.setposition(x,y)
#sets up values you'll need next row
RN = -2.0
i = i-.02
t.penup()
x = -200
y = y - 2
t.setposition(x,y)
t.penup()
#purely decorative black background for the set
def borders():
t.color("black")
t.setposition(-200,200)
t.pendown()
t.begin_fill()
for i in range(4):
t.forward(400)
t.right(90)
t.end_fill()
precision = 400
check_rate = float(400 / precision)
move_rate = float(check_rate/100)
t.penup()
borders()
t.setposition(x,y)
Mandelbrot(RN,pattern,precision,check_rate,move_rate,x,y,i,c)发布于 2020-03-01 06:29:43
哇!真是太慢了。我把range(100)改成了range(5),只是为了得到一些计时数字,而不是因为无聊而死。因此,以这作为我的基线,让我们努力加速它。
def set_color(RN,pattern,z,c):
for v in range(10):
z = z**2 + c
pattern.append(z)
if abs(z) > 3.7918623e+90:
t.color("#191970")
elif abs(z) > 1.9472705e+45:
t.color("#16167F")
elif abs(z) > 4.4127888e+22:
t.color("#13138F")
elif abs(z) > 210066388901:
t.color("#10109F")
elif abs(z) > 458330:
t.color("#0D0DAF")
elif abs(z) > 677:
t.color("#0B0BBF")
elif abs(z) > 26:
t.color("#0808CF")
elif abs(z) > 5:
t.color("#0505DF")
elif abs(z) > 2:
t.color("#0202EF")
elif abs(z) > 1.75:
t.color("yellow")
elif abs(z) > 1.5:
t.color("gold")
elif abs(z) > 1.25:
t.color("goldenrod")
elif abs(z) > 1:
t.color("darkgoldenrod")
else:
t.color("black")第一个参数RN不在此函数中使用。因此,我们可以省略这一点,并节省一点时间,而不是传递无用的论点。
z = 0
set_color(RN,pattern,z,c)
pattern = []第二个论点,pattern,正在累积,然后被丢弃。因此,需要进行大量的工作,包括内存分配,以扩展从未使用过的列表。我们可以删除这个论点和pattern.append(z)。
第三个论点,z,总是以零的形式出现。将其作为参数传递没有意义;只需在set_color函数中初始化它,这将节省一点时间,因为我们没有传递额外的参数。
这只剩下传递参数c。越来越好了。
然后,您有一个长串的if abs(z) > #检查。您计算一个复数的abs多少次?结果会有多少次不同?也许我们能计算一次?
abs_z = abs(z)
if z > 3.7918623e+90:
t.color("#191970")
elif abs_z > 1.9472705e+45:
...不幸的是,这仍然留下了一长串丑陋的if/elif/elif/else语句。如果有14种可能性,平均需要7次测试来确定正确的值;通过二进制搜索,这将减少到4次。我们只需要将值放入列表中,并使用bisect.bisect来确定abs(z)将被插入列表的位置。
import bisect
MAG = (1, 1.25, 1.5, 1.75, 2, 5,
26, 677, 458330, 210066388901, 4.4127888e+22, 1.9472705e+45,
3.7918623e+90)
COLOR = ("black", "darkgoldenrod", "goldenrod", "gold", "yellow", "#0202EF",
"#0505DF", "#0808CF", "#0B0BBF", "#0D0DAF", "#10109F", "#13138F",
"#16167F", "#191970")
def set_color(c):
z = 0
for _ in range(10):
z = z**2 + c
t.color(COLOR[bisect.bisect(MAG, abs(z))])我的时间还没有显示出对速度的显著影响,但我现在肯定更喜欢set_color函数了。如果您想要添加更多的颜色,这只是一个在数组中填充附加值的问题。
def Mandelbrot(RN,pattern,precision,check_rate,move_rate,x,y,i,c):
#draws line by line, 100 times
for e in range(100):
t.pendown()
#draws one line
for e in range(precision):
set_color(c) # Note: modified for new set_color
RN = float(RN + move_rate)
c = complex(RN,i)
x = x + check_rate
t.setposition(x,y)
#sets up values you'll need next row
RN = -2.0
i = i-.02
t.penup()
x = -200
y = y - 2
t.setposition(x,y)
t.penup()这看上去倒过来了。传入RN,但RN是在循环中计算的。传入x,但是每次通过循环将x初始化为-200。传入c,但c是在循环中计算的。而且大多数事情似乎是在下一个循环迭代使用之后才计算出来的!
让我们重新研究这个问题,把计算移到使用之前,看看它是什么样子。在此过程中,variable = variable + adjustment将被variable += adjustment所取代,因为它在Python解释器中减少了一个变量查找。而且,RN + move_rate已经是一个float,所以我们可以省略冗余的float()调用。当循环遍历一个值范围(y从99开始,下降两次100次)时,将使用for y in range(...)构造:
def mandelbrot(precision, check_rate, move_rate):
i = 1.0
t.penup()
for y in range(99, 99 - 2 * 100, -2):
rn = -2.0
x = -200
t.setposition(x, y)
t.pendown()
for _ in range(precision):
c = complex(rn, i)
set_color(c)
rn += move_rate
x += check_rate
t.setposition(x, y)
i -= 0.02
t.penup()几个PEP-8更改:逗号后面跟着空格,变量名和方法名是小写(从技术上讲,是snake_case)。_用作未使用的变量。操作符两边都有一个空间。
同样,float( )调用也是不必要的。
if __name__ == '__main__':
precision = 400
check_rate = 400 / precision
move_rate = check_rate / 100
t.penup()
borders()
mandelbrot(precision, check_rate, move_rate)尽管我最初的努力,我非常粗略的时间测量没有显示出我已经加快了任何事情。但我确实认为密码已经被大大地清理了。
我下一步的工作是将其分为两部分:
然后,我可以得到更准确的时间信息的每一节。
步骤1对并行处理开放(Python线程不会有帮助,因为全局解释器锁(GIL),但是使用进程应该会有所帮助),但是只有当它是大量的时间消耗者时才会有帮助。恐怕是海龟的图形。
https://codereview.stackexchange.com/questions/238141
复制相似问题