Как ускорить цикл Python

Я просмотрел несколько дискуссий на нескольких сайтах, и ни один из них не дал мне решения. Этот фрагмент кода выполняется более 5 секунд:

for i in xrange(100000000):
  pass

Я работаю над задачей целочисленной оптимизации и должен использовать алгоритм O(n log n) edit : алгоритм O(n²/4), где n обозначает всю матрицу ' элементов, то есть в следующем коде n * m = 10000. Таким образом, для матрицы 100 * 100 с 10000 элементов это приведет к почти 25000000 итераций.... Его код можно резюмировать следующим образом:

m = 100
n = 100
for i in xrange(m):
  for j in xrange(n):
    for i2 in xrange(i + 1, m):
      for j2 in xrange(j + 1, n):
        if myarray[i][j] == myarray[i2][j2] and myarray[i2][j] == myarray[i][j2]:
          return [i, j], [i2, j2]

Должен ли я отказаться от Python и вернуться к Java или C?

Я работаю с Python 2.7, а Psyco недоступен. PyPy не поддерживает Tkinter из коробки, и я использую Tkinter.

Итак, повысят ли они скорость зацикливания? Есть ли другие решения?


person s_xavier    schedule 07.01.2012    source источник
comment
Вы пытались реализовать это с помощью массивов numpy, если это возможно?   -  person joaquin    schedule 07.01.2012
comment
PyPy, безусловно, ускоряет такие циклы.   -  person    schedule 07.01.2012
comment
Вы можете ускорить свои циклы сколько угодно, приведенный выше код не O (n log n).   -  person John Percival Hackworth    schedule 07.01.2012
comment
И pypy ускорит процесс, но в 4-5 раз. Такой цикл должен занимать менее 0,5 секунды на приличном компьютере, если он написан на c.   -  person s_xavier    schedule 07.01.2012
comment
Похоже, что это алгоритм n^2*m^2, и вы не можете сделать много оптимизации, чтобы ускорить его на конкретном языке. Вы получите гораздо большую выгоду, если пересмотрите свой алгоритм и посмотрите, нет ли ненужных итераций.   -  person Makoto    schedule 07.01.2012
comment
Да, но, как выяснил Пол Макгуайр, я ищу прямоугольники и не могу делать меньше итераций... Этот алгоритм будет работать быстрее в C или в Java, потому что итерация цикла не требует столько циклов процессора, как в Python ...   -  person s_xavier    schedule 07.01.2012
comment
Просто чтобы было ясно, о чем вы спрашиваете: ваш алгоритм исправлен, и вы не будете его менять, и хотите знать, какой язык лучше всего подходит для вашей задачи? Или вам нужна оптимизированная версия вашего алгоритма?   -  person Don Question    schedule 08.01.2012
comment
Это исправлено, и я хочу знать, как сделать это быстрее в Python. Я начал использовать cython сегодня днем. Это часть большой программы, но остальной код не нуждается в оптимизации.   -  person s_xavier    schedule 08.01.2012


Ответы (5)


Не самый красивый стиль кодирования, но отчаянные времена требуют отчаянного кодирования. Попробуйте превратить ваши вложенные вложенные циклы в одно большое выражение генератора:

try:
    i,j,i2,j2 = ((i,j,i2,j2)
        for i in xrange(m)
          for j in xrange(n)
            for i2 in xrange(i + 1, m)
              for j2 in xrange(j + 1, n)
                if myarray[i][j] == myarray[i2][j2] and 
                    myarray[i2][j] == myarray[i][j2]).next()
    return [i,j],[i2,j2]
except StopIteration:
    return None

Обновлено для использования встроенных функций next и product, а также Py3 range вместо xrange:

from itertools import product
match = next(((i,j,i2,j2)
    for i, j in product(range(m), range(n))
        for i2, j2 in product(range(i+1, m), range(j+1, n))
            if myarray[i][j] == myarray[i2][j2] and 
                myarray[i2][j] == myarray[i][j2]), None)
if match is not None:
    i,j,i2,j2 = match
    return [i,j],[i2,j2]
else:
    return None
person PaulMcG    schedule 07.01.2012
comment
Это не улучшило скорость, но в любом случае приятное выражение лица. - person s_xavier; 07.01.2012
comment
В данный момент не могу проголосовать, нужно еще 5 очков репутации :/ - person s_xavier; 08.01.2012

Вы по-прежнему можете использовать нотацию Python и иметь скорость C, используя проект Cython. Первым шагом будет преобразование цикла в цикл C: это делается автоматически путем ввода всех переменных, используемых в цикле:

cdef int m = 100
cdef int n = 100
cdef int i, j, i2, j2
for i in xrange(m):
  for j in xrange(n):
    for i2 in xrange(i + 1, m):
      for j2 in xrange(j + 1, n):

Для следующей части было бы еще лучше, если бы этот myarray был чистым массивом C, тогда не было бы ни богатого сравнения python, ни доступа к массиву. С вашим массивом python вы можете удалить богатое сравнение, выполнив собственное сравнение (если у вас есть двойной массив):

        cdef double a, b, c, d
        a = myarray[i][j]
        b = myarray[i2][j2]
        c = myarray[i2][j]
        d = myarray[i][j2]

        if a == b and c == d:
          return [i, j], [i2, j2]

Вы можете увидеть, как идет оптимизация, запустив cython -a yourfile.pyx, а затем открыв yourfile.html generate. Вы увидите, как Cython оптимизировал ваш код и устранил накладные расходы Python.

person tito    schedule 07.01.2012
comment
Хорошо, спасибо, я попробую cython, потому что мне нужен большой прирост скорости. - person s_xavier; 07.01.2012
comment
На этой странице подробно описаны преимущества cython с проблемой, похожей на ту, с которой я имею дело: korokithakis.net/posts/optimizing-python-with-cython - person s_xavier; 07.01.2012
comment
Я начал использовать cython и numpy для массивов. Циклы действительно быстрее. Но у меня есть проблема: у меня есть вызовы функций, которые замедляют циклы, поэтому у меня много работы с cython. - person s_xavier; 08.01.2012
comment
вызов функции в python стоит дорого. Изучите Cython больше, и вы увидите, насколько это круто :) - person tito; 08.01.2012

Извините, что говорю вам, но это не вопрос оптимизации. Независимо от того, какой язык или реализацию вы выберете, этот алгоритм не будет O(n*log n) в худшем и среднем случае. Возможно, вы захотите прочитать как работает нотация Big-O и разработать лучший алгоритм.

person Niklas B.    schedule 07.01.2012
comment
Хорошо, это O(n²/4), а не O(n log n), но других алгоритмов нет, и это проблема оптимизации (подробности см. в редактировании в основном посте). - person s_xavier; 07.01.2012

Извините, генератор expr не работал. Вот другая схема, которая сначала подсчитывает все одинаковые значения, а затем ищет прямоугольные группы:

from collections import defaultdict
from itertools import permutations
def f3(myarray):
    tally = defaultdict(list)
    for i,row in enumerate(myarray):
        for j,n in enumerate(row):
            tally[n].append((i,j))

    # look for rectangular quads in each list
    for k,v in tally.items():
        for quad in permutations(v,4):
            # sort quad so that we can get rectangle corners 
            quad = sorted(quad)
            (i1,j1),(i2,j2) = quad[0], quad[-1]

            # slice out opposite corners to see if we have a rectangle
            others = quad[1:3]

            if [(i1,j2),(i2,j1)] == others:
                return [i1,j1],[i2,j2]
person PaulMcG    schedule 07.01.2012
comment
спасибо, я попробовал и узнал кое-что новое о синтаксисе Python, но он не работал быстрее, чем другие вещи. - person s_xavier; 07.01.2012
comment
Если у вас есть разреженный массив и вам нужны только совпадающие значения, отличные от нуля, вы можете пропустить все проверки перестановки нулевых ячеек, предварив оператор for quad in permutations(v,4): if k == 0: continue. - person PaulMcG; 07.01.2012

Я несколько раз просматривал ваш код, и если я правильно понял, вы отмечаете свои прямоугольники двумя разными угловыми маркерами. Извините, если мой ответ является скорее комментарием для разъяснения вашей позиции, чем реальным ответом.

Часть ответа:: если вы ищете подходящий алгоритм, рассмотрите алгоритм сканирования строк. Я нашел пример здесь @SO для "самого большого прямоугольного решения".

Мой вопрос к вам, что вы действительно ищете?

  1. лучший способ решить дилемму цикла for
  2. лучший язык для вашего алгоритма
  3. более быстрый алгоритм поиска прямоугольников

Я также должен отметить, что решение Пола и ваше решение дают разные результаты, потому что Полс предполагает, что углы отмечены одними и теми же значениями, а вы предполагаете, что углы отмечены двумя разными значениями!

Я нашел время и свободу, чтобы проиллюстрировать это уродливым сценарием c&p: он сравнивает оба алгоритма, создавая 2D-поле и заполняя его строчными символами в нижнем регистре, а углы превращает в символы в верхнем регистре.

from random import choice
from string import lowercase
from collections import defaultdict
from itertools import permutations
from copy import deepcopy

m,n = 20, 20
myarray = [[choice(lowercase) for x in xrange(m)] for y in xrange(n)]

def markcorners(i,j,i2,j2):
    myarray[i][j] = myarray[i][j].upper()
    myarray[i2][j] = myarray[i2][j].upper()
    myarray[i2][j2] = myarray[i2][j2].upper()
    myarray[i][j2] = myarray[i][j2].upper()

def printrect():
    for row in myarray:
        print ''.join(row)
    print '*'*m

def paul_algo():
    tally = defaultdict(list)
    for i,row in enumerate(myarray):
        for j,n in enumerate(row):
            tally[n].append((i,j))

    # look for rectangular quads in each list
    for k,v in tally.items():
        for quad in permutations(v,4):
            # sort quad so that we can get rectangle corners 
            quad = sorted(quad)
            (i1,j1),(i2,j2) = quad[0], quad[-1]

            # slice out opposite corners to see if we have a rectangle
            others = quad[1:3]

            if [(i1,j2),(i2,j1)] == others:
                markcorners(i1,j1,i2,j2)

def xavier_algo():   
    for i in xrange(m):
        for j in xrange(n):
            for i2 in xrange(i + 1, m):
                for j2 in xrange(j + 1, n):
                    if myarray[i][j] == myarray[i2][j2] and myarray[i2][j] == myarray[i][j2]:
                        markcorners(i,j,i2,j2)

savearray = deepcopy(myarray)
printrect()

xavier_algo()
printrect()

myarray = deepcopy(savearray)
paul_algo()
printrect()
person Don Question    schedule 08.01.2012
comment
Привет. Спасибо за Ваш ответ. Мне нужно, чтобы значения противоположных углов были разными. Было бы неплохо иметь лучший алгоритм. Я посмотрю на тот, который вы указываете завтра. Пора спать, потому что здесь уже поздно. - person s_xavier; 08.01.2012
comment
Я посмотрел на алгоритм, который вы предложили. Хорошая мысль, но она не соответствует моим особым потребностям. Мне не нужно искать самый большой прямоугольник, но я должен найти все прямоугольники с противоположными углами, которые имеют одинаковое значение. Он используется в функции соседства при поиске табу для задачи дискретной оптимизации. - person s_xavier; 08.01.2012