首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mandelbrot集的表示

Mandelbrot集的表示
EN

Code Review用户
提问于 2020-02-28 22:48:18
回答 1查看 136关注 0票数 3

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

最后图像:

代码语言:javascript
复制
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)
EN

回答 1

Code Review用户

回答已采纳

发布于 2020-03-01 06:29:43

哇!真是太慢了。我把range(100)改成了range(5),只是为了得到一些计时数字,而不是因为无聊而死。因此,以这作为我的基线,让我们努力加速它。

set_color

代码语言:javascript
复制
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不在此函数中使用。因此,我们可以省略这一点,并节省一点时间,而不是传递无用的论点。

代码语言:javascript
复制
        z = 0
        set_color(RN,pattern,z,c)
        pattern = []

第二个论点,pattern,正在累积,然后被丢弃。因此,需要进行大量的工作,包括内存分配,以扩展从未使用过的列表。我们可以删除这个论点和pattern.append(z)

第三个论点,z,总是以零的形式出现。将其作为参数传递没有意义;只需在set_color函数中初始化它,这将节省一点时间,因为我们没有传递额外的参数。

这只剩下传递参数c。越来越好了。

然后,您有一个长串的if abs(z) > #检查。您计算一个复数的abs多少次?结果会有多少次不同?也许我们能计算一次?

代码语言:javascript
复制
    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)将被插入列表的位置。

代码语言:javascript
复制
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函数了。如果您想要添加更多的颜色,这只是一个在数组中填充附加值的问题。

Mandelbrot

代码语言:javascript
复制
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(...)构造:

代码语言:javascript
复制
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( )调用也是不必要的。

代码语言:javascript
复制
if __name__ == '__main__':
    precision = 400
    check_rate = 400 / precision
    move_rate = check_rate / 100

    t.penup()
    borders()
    mandelbrot(precision, check_rate, move_rate)

提速?

尽管我最初的努力,我非常粗略的时间测量没有显示出我已经加快了任何事情。但我确实认为密码已经被大大地清理了。

我下一步的工作是将其分为两部分:

  1. 计算Mandelbrot颜色指标值的网格
  2. 用海龟图形绘制图像(使用彩色索引值的计算网格)

然后,我可以得到更准确的时间信息的每一节。

步骤1对并行处理开放(Python线程不会有帮助,因为全局解释器锁(GIL),但是使用进程应该会有所帮助),但是只有当它是大量的时间消耗者时才会有帮助。恐怕是海龟的图形。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/238141

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档