У меня есть сетка плиток известного конечного размера, которая формирует карту. Некоторые из тайлов внутри карты помещены в набор, известный как территория. Эта территория связана, но ничего не известно о ее форме. Большую часть времени это был бы довольно регулярный шарик, но он мог бы быть очень вытянутым в одном направлении и потенциально мог бы даже иметь отверстия. Я заинтересован в поиске (внешней) границы территории.
То есть я хочу получить список всех плиток, которые касаются одной из плиток на территории, но при этом не находятся на этой территории. Какой эффективный способ найти это?
Для дополнительной сложности, случается, что мои плитки являются гексами, но я подозреваю, что это не имеет большого значения, каждая плитка по-прежнему помечена целыми координатами x и y, и, учитывая плитку, я легко могу найти ее соседей. Ниже приведено несколько примеров: черный цвет - это территория, а синий - граница, которую я хочу найти. Само по себе это не является сложной проблемой. Один простой алгоритм для этого в псевдопифонах:
def find_border_of_territory(territory):
border = []
for tile in territory:
for neighbor in tile.neighbors():
if neighbor not in territory and neighbor not in border:
border.add(neighbor)
Однако это медленно, и я хотел бы что-то лучше. У меня есть цикл O (n) над территорией, другой цикл (короткий, но все же) по всем соседям, и затем я должен проверить членство по двум спискам, один из которых имеет размер n. Это дает ужасное масштабирование O (n ^ 2). Я могу уменьшить это до O (n), используя наборы вместо списков для границы и территории, чтобы быстро проверять членство, но все равно оно невелико. Я ожидаю, что будет много случаев, когда территория большая, но граница небольшая из-за простого масштабирования области против линии. Например, если территория представляет собой гекс с радиусом 5, она имеет размер 91, а граница - только размер 36.
Кто-нибудь может предложить что-то лучше?
Редактировать:
Чтобы ответить на некоторые вопросы ниже. Территория может варьироваться в размерах, от 20 до 100 или около того. Набор плиток, образующих территорию, является атрибутом объекта, и именно этому объекту требуется набор всех граничных плиток.
Первоначально территория создается как блок, а затем в основном получает плитки один за другим. В этом случае верно, что самый быстрый способ - просто сохранить набор границ и обновлять его только на полученной плитке. Время от времени могут происходить большие изменения на территории, поэтому тогда их необходимо будет полностью пересчитать.
Сейчас я придерживаюсь мнения, что простой алгоритм поиска границ - лучшее решение. Единственная дополнительная сложность, которую это вызывает, состоит в том, чтобы гарантировать, что граница пересчитывается каждый раз, когда это может потребоваться, но не более того. Я довольно уверен, что это может быть надежно сделано в моей нынешней структуре.
Что касается времени, в моем текущем коде у меня есть некоторые подпрограммы, которые должны проверить каждую плитку территории. Не на каждом шагу, но на создание и иногда после. Это занимает более 50% времени выполнения моего набора тестового кода, хотя это очень малая часть всей программы. Поэтому я стремился минимизировать любые повторения. ОДНАКО, тестовый код включает в себя гораздо больше создания объектов, чем нормальный запуск программы (естественно), поэтому я понимаю, что это может быть не очень актуально.