运行 多只海龟在 Python 上加速
Running Multiple Turtles Speed on Python
我正在尝试在python中构建一个核反应堆模型(不是很精确,只是为了学习和娱乐)。我正在关注这个 model.
到目前为止,我已经构建了基本的主框架。燃料、中子,以及基本的东西,比如木板和边框。正如您所知,当一个中子撞击适当的元素时,它能够将该元素一分为二,并产生一个(或几个)更多的中子。我在我的代码中应用了相同的概念,当一个中子撞击一个燃料粒子时,将产生另一个中子。但我现在面临的问题是,当我在屏幕上看到一定数量的中子时,模拟开始变慢,直到无法忍受观看为止。
我一直在查看我的代码,试图让它更有效率,但我找不到会导致这种情况的特定或特殊的东西。
我的代码:
import turtle
from random import randint
class Reactor:
def __init__(self, spendfuel, board, startNeut, iterr, percent_fuel):
self.fuel = []
self.fuel_t = self.newParticle('red','square',0,0)
self.spendfuel = spendfuel
turtle.setup(board[0]+200,board[1]+200), turtle.title("Reactor Top Down Reaction Model")
self.fuel,self.neutrons = self.setup(percent_fuel,board[0]//2,board[1]//2,1)
for i in range(iterr):
self.react(board[0]//2, board[1]//2)
if (len(self.neutrons) == 0):
return
turtle.update()
def setup(self, percent_fuel, x_length, y_length, neutronsNum):
turtle.bgcolor("black"), turtle.tracer(0,0)
for row in range(-x_length,x_length,4):
for column in range(y_length,-y_length,-4):
if (percent_fuel > randint(0,100)):
self.fuel_t.goto(row,column)
s_id = self.fuel_t.stamp()
s_pos = self.fuel_t.pos()
self.fuel.append([s_id,s_pos])
self.fuel_t.color('sienna')
self.neutrons = [ self.newParticle('yellow','circle',randint(-x_length,x_length),randint(-y_length,y_length)) for neutron in range(neutronsNum)]
turtle.update()
return self.fuel,self.neutrons
def react(self, x_length, y_length):
self.power = 0
for index,neutron in enumerate(self.neutrons):
x_pos = int(neutron.xcor())
y_pos = int(neutron.ycor())
inside_border = False
if ((-x_length <= x_pos) and (x_pos <= x_length) and (-y_length <= y_pos) and (y_pos <= y_length)):
inside_border = True
neutron.fd(2)
start = 0
if (x_pos <= 0 and y_pos >= 0): #Start the search for a nearby uranim from the current neutron's quad.
start = 0
elif (x_pos < 0 and y_pos < 0):
start = len(self.fuel) // 4
elif (x_pos > 0 and y_pos > 0):
start = len(self.fuel) // 2
else:
start = int(len(self.fuel) // 1.3333)
for i in range(start,len(self.fuel)-1):
if (neutron.distance(self.fuel[i][1]) <= 1):
self.fission(neutron,i,self.neutrons)
break
if not(inside_border):
self.neutrons.remove(neutron)
neutron.ht()
def fission(self, neutron, index, neutrons):
neutron.rt(randint(0,360))
if (self.spendfuel):
self.fuel_t.goto(self.fuel[index][1])
self.fuel_t.stamp()
self.fuel.pop(index)
neutrons.append(self.newParticle('yellow','circle',neutron.xcor(),neutron.ycor()))
neutrons[-1].rt(randint(0,360))
def newParticle(self, color, shape, row, column):
t = turtle.Pen() #New turltle type object
t.pu(), t.speed(10), t.ht(), t.color(color), t.shape(shape), t.shapesize(0.125,0.125,0.125)
t.goto(row,column), t.st()
return t
if __name__ == "__main__":
g = Reactor(False, [400,400], 1, 300, 10)
如果能帮助我解决这个问题,让我的模型 运行 更快,我将不胜感激。同样重要的是,与 turtle.stamp()
的燃料粒子不同,中子是乌龟物体。中子用黄色表示,而燃料粒子用红色表示
这个电话是你的瓶颈之一(可能占你时间的 2/3):
if (neutron.distance(self.fuel[i][1]) <= 1):
它发生了数十万次(如果参数正确,可能发生数百万次)并且它的核心是进行昂贵的运算:
(self[0]**2 + self[1]**2)**0.5
当它对 Vec2D 减法的结果调用 abs()
时。 (它甚至会测试 self.fuel[i][1]
是否是一个 Vec2D,当你知道它是时。)由于我们的目标是 <= 1
,我们可能不需要求幂和平方根,我们也许可以用更便宜的方法逃脱近似值如:
distance = self.fuel[i][1] - neutron.position() # returns a Vec2D
if abs(distance[0]) + abs(distance[1]) <= 1:
将此瓶颈减少到大约 1/3 的时间。 (即测试边界正方形而不是测试边界圆。)
it is still relatively slow and I'd like it to be faster
我们将使用传统方法解决此问题,通过将 self.fuel
转换为稀疏矩阵而不是列表来权衡 space 以提高速度。这样我们就完全消除了搜索,只检查当前位置是否在燃料棒上:
from turtle import Turtle, Screen
from random import randint
BORDER = 100
MAGNIFICATION = 4
CURSOR_SIZE = 20
class Reactor:
def __init__(self, spendfuel, board, startNeut, iterations, percent_fuel):
width, height = board
screen = Screen()
screen.setup(width + BORDER * 2, height + BORDER * 2)
screen.setworldcoordinates(-BORDER // MAGNIFICATION, -BORDER // MAGNIFICATION, (width + BORDER) // MAGNIFICATION, (height + BORDER) // MAGNIFICATION)
screen.title("Reactor Top Down Reaction Model")
screen.bgcolor("black")
screen.tracer(0)
scaled_width, scaled_height = width // MAGNIFICATION, height // MAGNIFICATION
self.fuel = [[None for x in range(scaled_width)] for y in range(scaled_height)]
self.fuel_t = self.newParticle('red', 'square', (0, 0))
self.spendfuel = spendfuel
self.neutrons = []
self.setup(percent_fuel, scaled_width, scaled_height, startNeut)
screen.update()
for _ in range(iterations):
self.react(scaled_width, scaled_height)
if not self.neutrons:
break
screen.update()
screen.exitonclick()
def setup(self, percent_fuel, x_length, y_length, neutronsNum):
for row in range(x_length):
for column in range(y_length):
if percent_fuel > randint(0, 100):
self.fuel_t.goto(row, column)
self.fuel[row][column] = self.fuel_t.stamp()
self.fuel_t.color('sienna') # spent fuel color
for _ in range(neutronsNum):
neutron = self.newParticle('yellow', 'circle', (randint(0, x_length), randint(0, y_length)))
neutron.setheading(neutron.towards((0, 0)))
self.neutrons.append(neutron)
def react(self, x_length, y_length):
neutrons = self.neutrons[:]
for neutron in neutrons:
x_pos, y_pos = neutron.position()
if 0 <= x_pos < x_length and 0 <= y_pos < y_length:
x_int, y_int = int(x_pos), int(y_pos)
if self.fuel[x_int][y_int]:
self.fission(neutron, x_int, y_int)
neutron.forward(1)
else:
self.neutrons.remove(neutron)
neutron.hideturtle()
def fission(self, neutron, x, y):
if self.spendfuel:
self.fuel_t.clearstamp(self.fuel[x][y])
self.fuel_t.goto(x, y)
self.fuel_t.stamp()
self.fuel[x][y] = None
neutron.right(randint(0, 360))
new_neutron = neutron.clone()
new_neutron.right(randint(0, 360))
self.neutrons.append(new_neutron)
@staticmethod
def newParticle(color, shape, position):
particle = Turtle(shape, visible=False)
particle.shapesize(MAGNIFICATION / CURSOR_SIZE, outline=0)
particle.speed('fastest')
particle.color(color)
particle.penup()
particle.goto(position)
particle.showturtle()
return particle
if __name__ == "__main__":
g = Reactor(True, [400, 400], 1, 400, 5)
为了提高速度和风格,我对您的代码进行了许多其他修改。我还正式确定了您的放大倍率,这在您的原始代码中有些随意。
我正在尝试在python中构建一个核反应堆模型(不是很精确,只是为了学习和娱乐)。我正在关注这个 model.
到目前为止,我已经构建了基本的主框架。燃料、中子,以及基本的东西,比如木板和边框。正如您所知,当一个中子撞击适当的元素时,它能够将该元素一分为二,并产生一个(或几个)更多的中子。我在我的代码中应用了相同的概念,当一个中子撞击一个燃料粒子时,将产生另一个中子。但我现在面临的问题是,当我在屏幕上看到一定数量的中子时,模拟开始变慢,直到无法忍受观看为止。
我一直在查看我的代码,试图让它更有效率,但我找不到会导致这种情况的特定或特殊的东西。
我的代码:
import turtle
from random import randint
class Reactor:
def __init__(self, spendfuel, board, startNeut, iterr, percent_fuel):
self.fuel = []
self.fuel_t = self.newParticle('red','square',0,0)
self.spendfuel = spendfuel
turtle.setup(board[0]+200,board[1]+200), turtle.title("Reactor Top Down Reaction Model")
self.fuel,self.neutrons = self.setup(percent_fuel,board[0]//2,board[1]//2,1)
for i in range(iterr):
self.react(board[0]//2, board[1]//2)
if (len(self.neutrons) == 0):
return
turtle.update()
def setup(self, percent_fuel, x_length, y_length, neutronsNum):
turtle.bgcolor("black"), turtle.tracer(0,0)
for row in range(-x_length,x_length,4):
for column in range(y_length,-y_length,-4):
if (percent_fuel > randint(0,100)):
self.fuel_t.goto(row,column)
s_id = self.fuel_t.stamp()
s_pos = self.fuel_t.pos()
self.fuel.append([s_id,s_pos])
self.fuel_t.color('sienna')
self.neutrons = [ self.newParticle('yellow','circle',randint(-x_length,x_length),randint(-y_length,y_length)) for neutron in range(neutronsNum)]
turtle.update()
return self.fuel,self.neutrons
def react(self, x_length, y_length):
self.power = 0
for index,neutron in enumerate(self.neutrons):
x_pos = int(neutron.xcor())
y_pos = int(neutron.ycor())
inside_border = False
if ((-x_length <= x_pos) and (x_pos <= x_length) and (-y_length <= y_pos) and (y_pos <= y_length)):
inside_border = True
neutron.fd(2)
start = 0
if (x_pos <= 0 and y_pos >= 0): #Start the search for a nearby uranim from the current neutron's quad.
start = 0
elif (x_pos < 0 and y_pos < 0):
start = len(self.fuel) // 4
elif (x_pos > 0 and y_pos > 0):
start = len(self.fuel) // 2
else:
start = int(len(self.fuel) // 1.3333)
for i in range(start,len(self.fuel)-1):
if (neutron.distance(self.fuel[i][1]) <= 1):
self.fission(neutron,i,self.neutrons)
break
if not(inside_border):
self.neutrons.remove(neutron)
neutron.ht()
def fission(self, neutron, index, neutrons):
neutron.rt(randint(0,360))
if (self.spendfuel):
self.fuel_t.goto(self.fuel[index][1])
self.fuel_t.stamp()
self.fuel.pop(index)
neutrons.append(self.newParticle('yellow','circle',neutron.xcor(),neutron.ycor()))
neutrons[-1].rt(randint(0,360))
def newParticle(self, color, shape, row, column):
t = turtle.Pen() #New turltle type object
t.pu(), t.speed(10), t.ht(), t.color(color), t.shape(shape), t.shapesize(0.125,0.125,0.125)
t.goto(row,column), t.st()
return t
if __name__ == "__main__":
g = Reactor(False, [400,400], 1, 300, 10)
如果能帮助我解决这个问题,让我的模型 运行 更快,我将不胜感激。同样重要的是,与 turtle.stamp()
的燃料粒子不同,中子是乌龟物体。中子用黄色表示,而燃料粒子用红色表示
这个电话是你的瓶颈之一(可能占你时间的 2/3):
if (neutron.distance(self.fuel[i][1]) <= 1):
它发生了数十万次(如果参数正确,可能发生数百万次)并且它的核心是进行昂贵的运算:
(self[0]**2 + self[1]**2)**0.5
当它对 Vec2D 减法的结果调用 abs()
时。 (它甚至会测试 self.fuel[i][1]
是否是一个 Vec2D,当你知道它是时。)由于我们的目标是 <= 1
,我们可能不需要求幂和平方根,我们也许可以用更便宜的方法逃脱近似值如:
distance = self.fuel[i][1] - neutron.position() # returns a Vec2D
if abs(distance[0]) + abs(distance[1]) <= 1:
将此瓶颈减少到大约 1/3 的时间。 (即测试边界正方形而不是测试边界圆。)
it is still relatively slow and I'd like it to be faster
我们将使用传统方法解决此问题,通过将 self.fuel
转换为稀疏矩阵而不是列表来权衡 space 以提高速度。这样我们就完全消除了搜索,只检查当前位置是否在燃料棒上:
from turtle import Turtle, Screen
from random import randint
BORDER = 100
MAGNIFICATION = 4
CURSOR_SIZE = 20
class Reactor:
def __init__(self, spendfuel, board, startNeut, iterations, percent_fuel):
width, height = board
screen = Screen()
screen.setup(width + BORDER * 2, height + BORDER * 2)
screen.setworldcoordinates(-BORDER // MAGNIFICATION, -BORDER // MAGNIFICATION, (width + BORDER) // MAGNIFICATION, (height + BORDER) // MAGNIFICATION)
screen.title("Reactor Top Down Reaction Model")
screen.bgcolor("black")
screen.tracer(0)
scaled_width, scaled_height = width // MAGNIFICATION, height // MAGNIFICATION
self.fuel = [[None for x in range(scaled_width)] for y in range(scaled_height)]
self.fuel_t = self.newParticle('red', 'square', (0, 0))
self.spendfuel = spendfuel
self.neutrons = []
self.setup(percent_fuel, scaled_width, scaled_height, startNeut)
screen.update()
for _ in range(iterations):
self.react(scaled_width, scaled_height)
if not self.neutrons:
break
screen.update()
screen.exitonclick()
def setup(self, percent_fuel, x_length, y_length, neutronsNum):
for row in range(x_length):
for column in range(y_length):
if percent_fuel > randint(0, 100):
self.fuel_t.goto(row, column)
self.fuel[row][column] = self.fuel_t.stamp()
self.fuel_t.color('sienna') # spent fuel color
for _ in range(neutronsNum):
neutron = self.newParticle('yellow', 'circle', (randint(0, x_length), randint(0, y_length)))
neutron.setheading(neutron.towards((0, 0)))
self.neutrons.append(neutron)
def react(self, x_length, y_length):
neutrons = self.neutrons[:]
for neutron in neutrons:
x_pos, y_pos = neutron.position()
if 0 <= x_pos < x_length and 0 <= y_pos < y_length:
x_int, y_int = int(x_pos), int(y_pos)
if self.fuel[x_int][y_int]:
self.fission(neutron, x_int, y_int)
neutron.forward(1)
else:
self.neutrons.remove(neutron)
neutron.hideturtle()
def fission(self, neutron, x, y):
if self.spendfuel:
self.fuel_t.clearstamp(self.fuel[x][y])
self.fuel_t.goto(x, y)
self.fuel_t.stamp()
self.fuel[x][y] = None
neutron.right(randint(0, 360))
new_neutron = neutron.clone()
new_neutron.right(randint(0, 360))
self.neutrons.append(new_neutron)
@staticmethod
def newParticle(color, shape, position):
particle = Turtle(shape, visible=False)
particle.shapesize(MAGNIFICATION / CURSOR_SIZE, outline=0)
particle.speed('fastest')
particle.color(color)
particle.penup()
particle.goto(position)
particle.showturtle()
return particle
if __name__ == "__main__":
g = Reactor(True, [400, 400], 1, 400, 5)
为了提高速度和风格,我对您的代码进行了许多其他修改。我还正式确定了您的放大倍率,这在您的原始代码中有些随意。