Skip to main content
 首页 » 编程设计

python之在 80x60 RGB 像素阵列上优化生命游戏迭代

2025年05月04日18lori

好的,所以我有一段真正需要优化的 Python 代码。

  • 这是对小型(80x60 像素)图像的生命游戏迭代,并从中提取 RGB 值。
  • 目前正在使用嵌套 for 循环;我宁愿将那些 for 循环换成更快的 map() c 函数,但如果我这样做,我无法弄清楚如何获取 x、y 值或局部值超出了我需要定义的功能范围。
  • 使用 map() 会比当前的一组 for 循环更快吗?我怎样才能使用它并仍然得到 x,y?
  • 我目前使用 pygame Surfaces,我已经尝试过 surfarray/pixelarray 模块,但由于我正在更改/获取每个像素,它比 Surface.get_at( )/set_at().
  • 另外,稍微无关紧要...您认为如果 Python 不遍历数字列表而只是递增一个数字,就像在其他语言中一样,这会变得更快吗?为什么 python 不包含普通的 for() 以及它们的 foreach()?
  • 条件语句的数量可能也会让事情变得更慢,对吧?最慢的部分是检查邻居(它构建列表 n 的地方)...我用二维数组上的切片访问替换了整个位,但它不能正常工作。

代码的编辑版本:

xr = xrange(80) 
yr = xrange(60) 
# surface is an instance of pygame.Surface 
get_at = surface.get_at() 
set_at = surface.set_at() 
 
for x in xr: 
    # .... 
    for y in yr: 
        # ... 
        pixelR = get_at((x,y))[0] 
        pixelG = get_at((x,y))[1] 
        pixelB = get_at((x,y))[2] 
        # ... more complex stuff here which changes R,G,B values independently of each other 
        set_at((x,y),(pixelR,pixelG,pixelB)) 

函数的完整版本:

# xr, yr = xrange(80), xrange(60) 
def live(surface,xr,yr): 
    randint = random.randint 
    set_at = surface.set_at 
    get_at = surface.get_at 
    perfect = perfectNeighbours # 
    minN = minNeighbours        # All global variables that're defined in a config file. 
    maxN = maxNeighbours        # 
    pos = actual                # actual = (80,60) 
    n = [] 
    append = n.append 
    NEIGHBOURS = 0 
 
    for y in yr: # going height-first for aesthetic reasons. 
        decay = randint(1,maxDecay) 
        growth = randint(1,maxGrowth) 
 
        for x in xr: 
            r, g, b, a = get_at((x,y)) 
 
            del n[:] 
            NEIGHBOURS = 0 
 
            if x>0 and y>0 and x<pos[0]-1 and y<pos[1]-1: 
                append(get_at((x-1,y-1))[1]) 
                append(get_at((x+1,y-1))[1]) 
                append(get_at((x,y-1))[1]) 
                append(get_at((x-1,y))[1]) 
                append(get_at((x+1,y))[1]) 
                append(get_at((x-1,y+1))[1]) 
                append(get_at((x+1,y+1))[1]) 
                append(get_at((x,y+1))[1]) 
                for a in n: 
                    if a > 63: 
                        NEIGHBOURS += 1 
 
            if NEIGHBOURS == 0 and (r,g,b) == (0,0,0): pass 
            else: 
 
                if NEIGHBOURS < minN or NEIGHBOURS > maxN: 
                    g = 0 
                    b = 0 
                elif NEIGHBOURS==perfect: 
                    g += growth 
                    if g > 255: 
                        g = 255 
                        b += growth 
                        if b > growth: b = growth 
                else: 
                    if g > 10: r = g-10 
                    if g > 200: b = g-100 
                    if r > growth: g = r 
                    g -= decay 
                    if g < 0: 
                        g = 0 
                        b = 0 
                r -= 1 
                if r < 0: 
                    r = 0 
                set_at((x,y),(r,g,b)) 

请您参考如下方法:

让您的代码变慢的原因可能不是循环,而是它们非常快。

使您的代码运行缓慢的是函数调用的数量。例如

pixelR = get_at((x,y))[0] 
pixelG = get_at((x,y))[1] 
pixelB = get_at((x,y))[2] 

比(我猜是大约 3 倍)慢很多

r, g, b, a = get_at((x,y)) 

每次 get_at、set_at 调用都会锁定表面,因此使用可用方法直接访问像素会更快。看起来最合理的是 Surface.get_buffer .

使用 map 在您的示例中不起作用,因为您需要索引。对于少至 80 和 60 个数字,使用 range() 而不是 xrange() 甚至可能更快。