Поиск связанных ветвей из списка линейных сегментов

#python #geometry #shapely #line-segment

#python #геометрия #стройный #линия-сегмент

Вопрос:

Проблема

У меня есть список линейных сегментов:

 exampleLineSegments = [(1,2),(2,3),(3,4),(4,5),(5,6),(4,7),(8,7)]
  

Эти сегменты включают индексы соответствующей точки в отдельный массив.

Из этого подсписка видно, что существует точка ветвления (4). Итак, из этой точки ветвления выходят три разные ветви. (В других, более конкретных проблемах может быть / быть несколько точек ветвления для n ветвей.)

Цель

Моя цель — получить словарь, включающий информацию о существующих ветвях, например,:

 result = { branch_1: [1,2,3,4],
           branch_2: [4,5,6],
           branch_3: [4,7,8]}
  

Текущее состояние работы / проблемы

В настоящее время я сначала определяю точки ветвления, настраивая словарь для каждой точки и проверяя для каждой записи, найдено ли более 2 соседних точек. Это означает, что существует точка ветвления.

После этого я просматриваю все точки, возникающие из этих точек ветвления, проверяя наличие преемников и т.д.

В этих функциях есть несколько циклов for и, как правило, интенсивное «сканирование». Это не самое чистое решение, и если количество точек увеличивается, производительность тоже не так хороша.

Вопрос

Каков наилучший / самый быстрый / самый эффективный способ достижения цели в этом случае?

Комментарии:

1. содержит ли он только одну точку ветвления?

2. К сожалению, нет. В этом примере существует только одна точка ветвления. В других задачах может существовать несколько точек ветвления

3. хорошо, я понял. Я работаю над этим.

Ответ №1:

Я думаю, вы можете достичь этого, выполнив следующие шаги:

  1. используйте neighbors dict для хранения графика
  2. найдите все точки ветвления, количество соседей которых> 2
  3. начните с каждой точки ветвления и используйте dfs для поиска всех путей
 from collections import defaultdict
def find_branch_paths(exampleLineSegments):
    # use dict to store the graph
    neighbors = defaultdict(list)
    for p1, p2 in exampleLineSegments:
        neighbors[p1].append(p2)
        neighbors[p2].append(p1)

    # find all branch points
    branch_points = [k for k, v in neighbors.items() if len(v) > 2]

    res = []

    def dfs(cur, prev, path):
        # reach the leaf
        if len(neighbors[cur]) == 1:
            res.append(path)
            return
        for neighbor in neighbors[cur]:
            if neighbor != prev:
                dfs(neighbor, cur, path   [neighbor])

    # start from all the branch points
    for branch_point in branch_points:
        dfs(branch_point, None, [branch_point])

    return res
  

обновите iteration версию для больших данных, что может привести к a recursion depth problem :

 def find_branch_paths(exampleLineSegments):
    # use dict to store the graph
    neighbors = defaultdict(list)
    for p1, p2 in exampleLineSegments:
        neighbors[p1].append(p2)
        neighbors[p2].append(p1)

    # find all branch points
    branch_points = [k for k, v in neighbors.items() if len(v) > 2]

    res = []
    # iteration way to dfs
    stack = [(bp, None, [bp]) for bp in branch_points]
    while stack:
        cur, prev, path = stack.pop()

        if len(neighbors[cur]) == 1 or (prev and cur in branch_points):
            res.append(path)
            continue

        for neighbor in neighbors[cur]:
            if neighbor != prev:
                stack.append((neighbor, cur, path   [neighbor]))

    return res
  

тестирование и вывод:

 print(find_branch_paths([(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (4, 7), (8, 7)]))
# output: 
# [[4, 3, 2, 1], [4, 5, 6], [4, 7, 8]]
  

Надеюсь, это поможет вам, и прокомментируйте, если у вас возникнут дополнительные вопросы. 🙂

ОБНОВЛЕНИЕ: если точек ветвления много, путь будет расти экспоненциально. Поэтому, если вам нужны только отдельные сегменты, вы можете завершить путь, когда встретите другую точку ветвления.

измените эту строку
if len(neighbors[cur]) == 1:
Для
if len(neighbors[cur]) == 1 or (prev and cur in branch_points):

Комментарии:

1. привет @recnac, во-первых: Большое вам спасибо. Этот код выглядит таким чистым, а также отлично работает (и действительно быстро!). К сожалению, я перешел к своей самой большой проблеме и столкнулся с проблемой глубины рекурсии (раньше ее никогда не было). Есть ли хороший обходной путь, чтобы избежать этой ошибки? Файл «», строка 32, в dfs, если len(neighbors[cur]) == 1 или (предыдущие и текущие в branch_points): ошибка рекурсии: превышена максимальная глубина рекурсии для сравнения Я погуглил и нашел что-то вроде import sys sys.setrecursionlimit(3000) Но есть ли «более разумное» решение?

2. насколько велики ваши данные? Если длина пути больше максимальной глубины рекурсии, вы можете использовать iteration version вместо recursion version . Или реализуйте свой собственный stack вместо использования стека системных функций.

3. если ваши данные большие, вы можете записывать только отдельный путь, как я сказал выше.

4. Я напишу iteration версию для вас.

5. попробуйте iteration версию 🙂 @BennyS