Как ускорить цикл 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.

Итак, улучшат ли скорость цикла? Существуют ли другие решения?

Ответ 1

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

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

Ответ 2

Вы все еще можете использовать нотацию 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 вы можете удалить богатый comparaison, выполнив собственный comparaison (если у вас двойной размер в вашем массиве):

        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.

Ответ 3

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

Ответ 4

Извините, генератор 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]

Ответ 5

Я посмотрел несколько раз на ваш код, и, если я правильно понял, ваши маркировки ваших прямоугольников двумя разными угловыми маркерами. Прошу прощения, если мой ответ - скорее комментарий, чтобы прояснить вашу позицию, а затем действительно ответить.

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

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

  • лучший способ решить дилемму for-loop
  • лучший язык для вашего алгоритма
  • более быстрый алгоритм для поиска прямоугольников

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

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

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()