我如何优化此代码以使其执行得更快(最多 ~5 分钟)?

How do I optimize this code so that it executes faster (max ~5 mins)?

我正在尝试用 turtle 编写一个程序来创建 Lyapunov 分形。但是,正如使用 timeit 显示的那样,这应该需要大约 3 个小时才能完成,如果我妥协分辨率 (N),则需要 1.5 个小时。

import turtle as t; from math import log; from timeit import default_timer as dt
t.setup(2000,1000,0); swid=t.window_width(); shei=t.window_height(); t.up(); t.ht(); t.tracer(False); t.colormode(255); t.bgcolor('pink')

def lyapunov(seq,xmin,xmax,ymin,ymax,N,tico):
    truseq=str(bin(seq))[2:]
    for x in range(-swid//2+2,swid//2-2):
        tx=(x*(xmax-xmin))/swid+(xmax+xmin)/2
        if x==-swid//2+2:
            startt=dt()
        for y in range(-shei//2+11,shei//2-1):
            t.goto(x,y); ty=(y*(ymax-ymin))/shei+(ymax+ymin)/2; lex=0; xn=prevxn=0.5
            for n in range(1,N+1):
                if truseq[n%len(truseq)]=='0': rn=tx
                else: rn=ty
                xn=rn*prevxn*(1-prevxn)
                prevxn=xn
                if xn!=1: lex+=(1/N)*log(abs(rn*(1-2*xn)))
            if lex>0: t.pencolor(0,0,min(int(lex*tico),255))
            else: t.pencolor(max(255+int(lex*tico),0),max(255+int(lex*tico),0),0)
            t.dot(size=1); t.update()
        if x==-swid//2+2:
            endt=dt()
            print(f'Estimated total time: {(endt-startt)*(swid-5)} secs')

#Example: lyapunov(2,2.0,4.0,2.0,4.0,10000,100)

我尝试使用 yield,但我不知道它应该去哪里。

在我较慢的机器上,我只能用很小的 N(例如 10)进行测试,但我能够将代码加速大约 350 倍。 (尽管随着 N 的增加,这个值会明显降低。)使用 update() 有两个问题。首先是您调用它的频率太高了——您应该将它从 y 循环缩进到 x 循环,这样它在每次垂直传递时只调用一次。其次,dot() 运算符强制自动执行 update(),因此使用 tracer() 没有任何优势。将 dot() 替换为其他绘制像素的方法,您将恢复使用 tracer()update() 的优势。 (只要你像我指出的那样将 update() 移出最内层循环。)

我对您的代码进行了修改,其中我尝试了这些和其他更改:

from turtle import Screen, Turtle
from math import log
from timeit import default_timer

def lyapunov(seq, xmin, xmax, ymin, ymax, N, tico):
    xdif = xmax - xmin
    ydif = ymax - ymin

    truseq = str(bin(seq))[2:]

    for x in range(2 - swid_2, swid_2 - 2):
        if x == 2 - swid_2:
            startt = default_timer()

        tx = x * xdif / swid + xdif/2

        for y in range(11 - shei_2, shei_2 - 1):
            ty = y * ydif / shei + ydif/2
            lex = 0
            xn = prevxn = 0.5

            for n in range(1, N+1):
                rn = tx if truseq[n % len(truseq)] == '0' else ty

                xn = rn * prevxn * (1 - prevxn)
                prevxn = xn

                if xn != 1:
                    lex += 1/N * log(abs(rn * (1 - xn*2)))

            if lex > 0:
                turtle.pencolor(0, 0, min(int(lex * tico), 255))
            else:
                lex_tico = max(int(lex * tico) + 255, 0)
                turtle.pencolor(lex_tico, lex_tico, 0)

            turtle.goto(x, y)
            turtle.pendown()

        turtle.penup()
        screen.update()

        if x == 2 - swid_2:
            endt = default_timer()
            print(f'Estimated total time: {(endt - startt) * (swid - 5)} secs')

screen = Screen()
screen.setup(2000, 1000, startx=0)
screen.bgcolor('pink')
screen.colormode(255)
screen.tracer(False)

swid = screen.window_width()
shei = screen.window_height()

swid_2 = swid//2
shei_2 = shei//2

turtle = Turtle()
turtle.hideturtle()
turtle.penup()
turtle.setheading(90)

lyapunov(2, 2.0, 4.0, 2.0, 4.0, 10, 100)

screen.exitonclick()