Эффективное манипулирование списком декартовых координат в Python

Справочная информация:

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

Вопрос:

Мне нужно реализовать новый фильтр, который эффективно проходит через координаты, сравнивая каждую пару с каждой другой парой, т.е. (x1,y1)(x2,y2) до (x1,y1)(xn,yn), (x2,y2)(x3,y3) до (x2,y2)(xn,yn) и т.д. для всех записей и, например, если соотношение между (x1,y1) и (x5,y5) соответствует [(x5-x1)^2+(y5-y1)^2]=vertex_spacing^2, тогда два набора координат затем соединяются с их соответствующим списком и добавляется в новый список, где одна запись будет иметь вид: [(x1,y1), (x5,y5), 0, 4] например. Каков наиболее эффективный метод достижения этого?

Мои попытки:

Я рассмотрел несколько методов обработки списков здесь и в разных руководствах. Я попытался использовать вложенные петли "for" и "if", но найти, пока этот метод может работать, приводит к чрезмерно длительному времени работы, а также пытается разбить проблему на множество меньших для циклов.

Дополнительные примечания:

Конечной целью этого является использование результирующих координат для интерфейсных элементов интерфейса и их сохранение и импорт по мере необходимости. Функция позиций 0 и 4 списка в [(x1,y1), (x5,y5), 0, 4] заключается в том, чтобы позволить интерфейсу группировать координаты для последующего использования в объектах холста. Метод должен иметь возможность обрабатывать потенциально тысячи координат.

Заранее благодарю за любую помощь, я, конечно же, желаю улучшить формулировку/информацию, которую я предоставил, и/или добавить примерный код, если неясно, что я прошу в любом случае - я все еще совершенно новый к этому!:)

Ответ 1

Что вы в основном проверяете:

для каждой вершины v, найдите все вершины u такие, что u находится на окружности радиуса vertex_spacing вокруг v.

Если распределение ваших точек таково, что не все точки близки друг к другу, я думаю о двух подходах к ускорению поиска:

  • Самый простой способ ускорить этот процесс - сортировать точки по координате x. Таким образом, вы можете пропустить много сравнений. В качестве простого примера предположим, что x-координаты [1, 2, 10, 15, 18, 20, 21] и vertex_spacing = 5. Вам нужно только сравнить первую вершину со второй, потому что все остальные вершины, очевидно, вне круга вокруг первой вершины.

    Обратите внимание, что этот подход бесполезен, если все точки близко друг к другу. Другими словами, если vertex_spacing = 25, вы не можете пропустить какое-либо сравнение.

  • В то же время вы можете использовать 2-мерное дерево k-d. Это эквивалентно подходу сортировки, но в двух измерениях. Учитывая вершину (x, y) и vertex_spacing = v, вам нужно будет проверить все точки в диапазоне ([x-v, x+v], [y-v, y+v]). Используя тот же пример, что и раньше, предположим, что первая точка имела координаты (1, 0), а вторая - (2, 10), нет необходимости сравнивать первую вершину с чем-либо.

Оба подхода являются эвристиками и не дают каких-либо улучшений в худшем случае (совсем наоборот: у вас также есть накладные расходы на сортировку/построение дерева kd), но если вершины обычно не менее vertex_space Кроме того, это может ускорить поиск.

Ответ 2

Я слишком медленно искал описание алгоритма Heuster, но здесь реализована реализация метода сортировки по координатам x:

def pairs(coords, vertex_spacing):
    results = []
    vsquared = vertex_spacing * vertex_spacing
    coords = sorted(coords)
    for ia, (xa, ya) in enumerate(coords):
        for ib, (xb, yb) in enumerate(coords[ia:]):
            dx = xb - xa
            if dx > vertex_spacing:
                break
            dy = yb - ya
            if dx * dx + dy * dy == vsquared:
                results.append([(xa, ya), (xb, yb), ia, ia + ib])
    return results

... и здесь он находится в действии:

>>> coords = list((x, y) for x in range(100) for y in range(100))
>>> p = pairs(coords, 5)
>>> from random import choice
>>> choice(p)
[(93, 36), (96, 40), 9336, 9640]
>>> choice(p)
[(9, 57), (13, 54), 957, 1354]
>>> choice(p)
[(46, 69), (46, 74), 4669, 4674]

На моей машине pairs(coords, 5) требуется 1,5 секунды, чтобы проверить 10000 координирующих пар (и 0,15 секунды для проверки 2,500).

РЕДАКТИРОВАТЬ: я забыл добавить ia в ib, чтобы компенсировать перечисление по фрагменту - теперь исправлено.

Ответ 3

Самые медленные части вашего алгоритма - это отдельная обработка координат x и y и вычисление гипотенузы. Оба эти могут быть ускорены с помощью собственного набора типов Python:

>>> from itertools import starmap
>>> parray = list(starmap(complex, [(5, 1), (8.5, 3), (3.75, 4.25)]))
>>> a = parray[0]
>>> b = parray[1]
>>> a
(5+1j)
>>> b
(8.5+3j)
>>> a-b
(-3.5-2j)
>>> abs(a-b)
4.031128874149275

Ответ 4

Один из способов ускорить работу - использовать какой-то пространственный индекс, чтобы вы исключали точки поиска, которые явно далеко друг от друга. Вот модуль, который может быть полезен: http://toblerity.org/rtree/. См. Также http://en.wikipedia.org/wiki/R-tree.