【问题标题】:Running Multiple Turtles Speed on Python在 Python 上运行多个海龟速度
【发布时间】:2018-05-04 20:13:49
【问题描述】:

我正在尝试用 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() 的燃料颗粒不同,是乌龟物体。中子用颜色表示——黄色——而燃料粒子用颜色表示——红色——

【问题讨论】:

  • 代码太多了。此外,它不会按原样运行。如果你修复了明显的缩进错误,它会运行,但会立即退出,所以它仍然不能证明你所问的问题。请阅读帮助中的minimal reproducible example
  • 我修复了缩进并放置了我的代码的旧版本始终可以工作,我之前遇到的版本在产生第一个中子时遇到了一些麻烦,对此感到抱歉。 @abarnert
  • 代码还是太多了。您需要弄清楚如何将其简化为证明问题所需的基本要素。没有这个,我可以做出一些猜测(例如,你是在裂变中子,然后在移除燃料之前检查新的中子吗?),但它们只是疯狂的猜测,没有大量的调试工作来缩小范围。
  • @OmerHen 您确实在“self.fission”行之外有一个(嵌套)循环。 if 条件很可能比您预期的更频繁地评估为真。我在那里放了一个打印声明,并得到了很多打印输出。将您的幻数从 2 调整为其他值。
  • 我缩小了代码范围。在我遵循的模型中(您可以在帖子中找到链接),默认设置是当中子撞击燃料时,它们不会浪费它,它们只是分裂并且燃料保持完整。因此,我尝试遵循相同的程序。 @abarnert

标签: python python-3.x turtle-graphics


【解决方案1】:

此调用是您的瓶颈之一(可能是您 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,如果你知道它是。)由于我们的目标是 &lt;= 1,我们可能不需要求幂和平方根,我们也许可以使用更便宜的近似值,例如:

distance = self.fuel[i][1] - neutron.position()  # returns a Vec2D

if abs(distance[0]) + abs(distance[1]) <= 1:

将这个瓶颈减少到大约 1/3 的时间。 (即测试边界正方形而不是测试边界圆。)

它仍然相对较慢,我希望它更快

我们将使用传统方法来解决这个问题,通过将self.fuel 转换为稀疏矩阵而不是列表来以空间换取速度。这样我们就完全消除了搜索,只检查当前位置是否位于燃料棒上:

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)

为了速度和风格,我对您的代码进行了许多其他修改。我还正式确定了您的放大倍数,这在您的原始代码中有些随意。

【讨论】:

  • 谢谢!我已经进行了您建议的更改,并且模拟似乎移动得更快。虽然它仍然相对较慢,但我希望它更快。我一直在做一些检查,就像你说的那样,if (neutron.distance(self.fuel[i][1]) &lt;= 1): 语句被调用了很多次,那是因为它位于嵌套的 for 循环中,该循环遍历每个中子的 self.fuel 数组。您是否有一个想法,也许我怎样才能找到附近的燃料,而不必检查阵列并单独检查每个燃料颗粒? @cdlane
  • @OmerHen,我通过修改您的代码来扩充我的答案,通过将燃料搜索设为稀疏矩阵而不是列表来消除燃料搜索。以及许多其他更改供您考虑。
  • 谢谢,这正是我正在寻找的解决方案。稀疏矩阵似乎确实以更快更有效的方式处理这种情况。对于糟糕的编码风格,我很抱歉,这是我最初几个版本的代码之一,因为我想发布一些简短的内容来解决问题。再次感谢! @cdlane
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
  • 2013-12-28
  • 1970-01-01
  • 2021-06-22
相关资源
最近更新 更多