Проблема оптимизации Python? - PullRequest
2 голосов
/ 15 мая 2010

Хорошо, у меня недавно было это домашнее задание (не волнуйтесь, я уже сделал это, но в C ++), но мне стало любопытно, как я могу сделать это в Python. Проблема в двух источниках света, которые излучают свет. Я не буду вдаваться в подробности.

Вот код (который мне удалось немного оптимизировать в последней части):

import math, array
import numpy as np
from PIL import Image

size = (800,800)
width, height = size

s1x = width * 1./8
s1y = height * 1./8
s2x = width * 7./8
s2y = height * 7./8

r,g,b = (255,255,255)
arr = np.zeros((width,height,3))
hy = math.hypot
print 'computing distances (%s by %s)'%size,
for i in xrange(width):
    if i%(width/10)==0:
        print i,    
    if i%20==0:
        print '.',
    for j in xrange(height):
        d1 = hy(i-s1x,j-s1y)
        d2 = hy(i-s2x,j-s2y)
        arr[i][j] = abs(d1-d2)
print ''

arr2 = np.zeros((width,height,3),dtype="uint8")        
for ld in [200,116,100,84,68,52,36,20,8,4,2]:
    print 'now computing image for ld = '+str(ld)
    arr2 *= 0
    arr2 += abs(arr%ld-ld/2)*(r,g,b)/(ld/2)
    print 'saving image...'
    ar2img = Image.fromarray(arr2)
    ar2img.save('ld'+str(ld).rjust(4,'0')+'.png')
    print 'saved as ld'+str(ld).rjust(4,'0')+'.png'

Мне удалось оптимизировать большую часть этого, но в части с 2 for-s все еще есть огромный разрыв в производительности, и я не могу придумать способ обойти это, используя обычные операции с массивами ... Я открыт для предложений: D

Edit: В ответ на предложение Влада я опубликую детали проблемы: Есть 2 источника света, каждый из которых излучает синусоидальную волну: E1 = E0 * грех (омега1 * время + фи01) E2 = E0 * грех (омега2 * время + фи02) мы рассматриваем omega1 = omega2 = omega = 2 * PI / T и phi01 = phi02 = phi0 для простоты считая x1 расстоянием от первого источника точки на плоскости, интенсивность света в этой точке Ep1 = E0 * sin (омега * время - 2 * PI * x1 / лямбда + phi0) где лямбда = скорость света * T (период колебаний) Учитывая оба источника света на плоскости, формула становится Ep = 2 * E0 * cos (PI * (x2-x1) / лямбда) sin (омега время - PI * (x2-x1) / лямбда + phi0) и из этого мы могли бы понять, что интенсивность света максимальна, когда (x2-x1) / лямбда = (2 * k) * PI / 2 и минимум, когда (x2-x1) / лямбда = (2 * k + 1) * PI / 2 и варьируется между, где k является целым числом

Для данного момента времени, с учетом координат источников света, и для известной лямбды и E0, мы должны были составить программу, чтобы нарисовать, как выглядит свет ИМХО, я думаю, что оптимизировал проблему настолько, насколько это возможно ...

Ответы [ 4 ]

5 голосов
/ 16 мая 2010

Шаблоны помех веселые, не правда ли?

Итак, во-первых, это будет незначительно, потому что запуск этой программы на моем ноутбуке занимает всего двенадцать с половиной секунд.

Но давайте посмотрим, что можно сделать с выполнением первого бита с помощью операций с массивами, не так ли? У нас есть в основном то, что вы хотите:

arr[i][j] = abs(hypot(i-s1x,j-s1y) - hypot(i-s2x,j-s2y))

Для всех i и j.

Итак, поскольку numpy имеет функцию hypot, которая работает с массивами numpy, давайте использовать это. Наша первая задача - получить массив правильного размера с каждым элементом, равным i, а другой - с каждым элементом, равным j. Но это не так уж сложно; на самом деле, ответ ниже указывает мне на чудесный numpy.mgrid, о котором я раньше не знал, который делает именно это:

array_i,array_j = np.mgrid[0:width,0:height]

Существует небольшой вопрос о том, чтобы сделать массив размером (width, height) в (width,height,3), чтобы он был совместим с вашими утверждениями генерации изображений, но это довольно легко сделать:

arr = (arr * np.ones((3,1,1))).transpose(1,2,0)

Затем мы подключаем это к вашей программе и позволяем выполнять операции с массивами:

import math, array
import numpy as np
from PIL import Image

size = (800,800)
width, height = size

s1x = width * 1./8
s1y = height * 1./8
s2x = width * 7./8
s2y = height * 7./8

r,g,b = (255,255,255)

array_i,array_j = np.mgrid[0:width,0:height]

arr = np.abs(np.hypot(array_i-s1x, array_j-s1y) -
             np.hypot(array_i-s2x, array_j-s2y))

arr = (arr * np.ones((3,1,1))).transpose(1,2,0)

arr2 = np.zeros((width,height,3),dtype="uint8")
for ld in [200,116,100,84,68,52,36,20,8,4,2]:
    print 'now computing image for ld = '+str(ld)
    # Rest as before

И новое время ... 8,2 секунды. Таким образом, вы экономите, может быть, четыре целых секунды. С другой стороны, сейчас это почти исключительно на этапах генерации изображений, поэтому, возможно, вы можете усилить их, создавая только те изображения, которые вам нужны.

3 голосов
/ 16 мая 2010

Если вы используете операции с массивами вместо циклов, это намного, намного быстрее. Для меня генерация изображений - это то, что занимает так много времени. Вместо ваших двух i,j петель у меня есть это:

I,J = np.mgrid[0:width,0:height]
D1 = np.hypot(I - s1x, J - s1y)
D2 = np.hypot(I - s2x, J - s2y)

arr = np.abs(D1-D2)
# triplicate into 3 layers
arr = np.array((arr, arr, arr)).transpose(1,2,0)
# .. continue program

Основы, которые вы хотите запомнить на будущее: это , а не об оптимизации; использование форм массива в numpy - это просто использование его так, как предполагается. Имея опыт, ваши будущие проекты не должны идти в обход циклов Python, формы массивов должны быть естественной формы.

То, что мы сделали здесь, было действительно просто. Вместо math.hypot мы нашли numpy.hypot и использовали его. Как и все подобные функции, он принимает ndarrays в качестве аргументов и делает именно то, что нам нужно.

2 голосов
/ 16 мая 2010

Понимание списка намного быстрее, чем зацикливание. Например, вместо

for j in xrange(height):
        d1 = hy(i-s1x,j-s1y)
        d2 = hy(i-s2x,j-s2y)
        arr[i][j] = abs(d1-d2)

Вы бы написали

arr[i] = [abs(hy(i-s1x,j-s1y) - hy(i-s2x,j-s2y)) for j in xrange(height)]

С другой стороны, если вы действительно пытаетесь «оптимизировать», вы можете переопределить этот алгоритм в C и использовать SWIG или тому подобное для вызова его из python.

1 голос
/ 16 мая 2010

Единственные изменения, которые приходят мне в голову, это вывести некоторые операции из цикла:

for i in xrange(width):
    if i%(width/10)==0:
        print i,    
    if i%20==0:
        print '.',
    arri = arr[i]
    is1x = i - s1x
    is2x = i - s2x
    for j in xrange(height):
        d1 = hy(is1x,j-s1y)
        d2 = hy(is2x,j-s2y)
        arri[j] = abs(d1-d2)

Улучшение, если оно вообще будет, вероятно, будет незначительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...