Поиск максимально возможной формы внутри сетки

Я пытаюсь создать алгоритм, который находит максимально возможную форму с заданным набором точек. Каждая точка может быть соединена с другой по вертикали, по горизонтали (на 1 единицу) или по диагонали. Формируется форма, где такие соединения между точками создают форму (очевидно) Поиск максимально возможной формы внутри сетки (не обращайте внимания на цвета)

Точка имеет индексы x и y, определяющие ее положение на доске.

Что я уже пробовал?

  1. Моей первой идеей было использовать алгоритм поиска пути, настроенный специально для поиска пути обратно к начальной точке в сетке точек, с единственной особенностью в том, что он пытается найти самый длинный путь, а не самый короткий. Это не удалось, потому что алгоритм поиска пути проходил через все возможные точки и не находил на самом деле самую большую фигуру.
  2. Моя вторая идея заключалась в том, чтобы найти точки одной и той же формы (не найти саму форму, а точки, которые подтверждены внутри нее), а затем найти выпуклую оболочку множества точек. Выпуклая оболочка дает мне набор точек, из которых я могу попытаться найти путь к следующей точке выпуклой оболочки, дающей мне форму. Я отказался от этой идеи из-за ее сложности, так как уже потерялся в теории этого метода.

Итак, что я могу попытаться достичь того, чего я пытаюсь сделать?

каково ваше определение biggest?

jsotola 13.06.2024 19:01

Если две фигуры касаются друг друга в одной точке, это одна фигура или две?

Matt Timmermans 13.06.2024 19:33

Самый большой - как в самой большой области. --------- Если две фигуры касаются друг друга, это действительно две разные фигуры. Одна фигура не может иметь перекрывающихся точек

Andrey Varvaryuk 13.06.2024 20:12

Привет. Не могли бы вы объяснить, почему просто взять выпуклую оболочку всего набора точек и создать одну большую фигуру не является решением этой проблемы? С помощью этих точек можно было бы создать максимально большую площадь! Из вашего вопроса я не понимаю, почему вы отклонили это решение, и поэтому не могу предлагать другие решения.

Stef 14.06.2024 14:59

Привет, причина, по которой выпуклая оболочка не является решением, заключается в том, что расстояние между точками выпуклой оболочки не гарантированно составляет 1 единицу (и по диагонали). Как я уже сказал, форма состоит из связей между точками, и эти связи есть только между точками, находящимися на расстоянии 1 единицы или по диагонали.

Andrey Varvaryuk 16.06.2024 18:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
5
141
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

С помощью python , и если я правильно понял, вы можете построить DistanceBand на основе метрики Чебышева , а затем сделать convex_hull из набора MultiPoint , сделанного из подключенные_компоненты:

from libpysal import weights
from shapely import MultiPoint

db = weights.DistanceBand.from_array(
    points,
    p=float("inf"), # "chebyshev"
    threshold=1,
    silence_warnings=True,
)

shapes = [
    MultiPoint([points[n] for n in cc]).convex_hull
    for cc in nx.connected_components(db.to_networkx())
    if len(cc) > 1
]

Примечание: точки 17 и 18 связаны (поскольку диагональ = 1), в отличие от того, что показано на вашем рисунке.

Выход (shapes):

[
    <POLYGON ((5 10, 4 11, 5 12, 6 11, 6 10, 5 10))>,
    <POLYGON ((12 10, 11 11, 12 12, 13 11, 13 10, 12 10))>,
    <POLYGON ((17 7, 16 9, 17 10, 19 10, 18 8, 17 7))>,
    <POLYGON ((21 9, 21 11, 22 11, 22 10, 21 9))>
]

С анимацией, подчеркивающей разницу между тремя видами расстояний:

Использовано points :

points = [
    (4, 11), (5, 10), (5, 11), (5, 12), (6, 10),
    (6, 11), (8, 12), (11, 11), (12, 10), (12, 12),
    (13, 10), (13, 11), (16, 9), (17, 7), (17, 8),
    (17, 10), (18, 8), (18, 9), (19, 10), (21, 9),
    (21, 10), (21, 11), (22, 10), (22, 11),
]

«Точки 17 и 18 связаны, в отличие от того, что показано на вашем рисунке». они не связаны, потому что окончательная форма должна состоять из соседних точек (форма — это все внешние точки). Точки 15 и 18 находятся на расстоянии 2 единиц друг от друга, поэтому связь между ними невозможна, следовательно, фигура недействительна. Это соединение «15->17->18->17->16», которое образует соединение между 17 и 18 дважды, также не является допустимой формой. Возможно, мне следовало уточнить, что окончательная форма не имеет перекрывающихся точек или соединений.

Andrey Varvaryuk 13.06.2024 20:09

Нет, 17 и 18 связаны на моей фигуре, а не на вашей. Также не обращайте внимания на края/связи между точками, это всего лишь промежуточный шаг перед созданием окончательных полигонов. Вы можете проверить этот, чтобы понять, что я имею в виду. В любом случае удачи ;)

Timeless 13.06.2024 20:16

Каждая из «форм», которые вы ищете, представляет собой контур двусвязного компонента на графике.

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

Следите за областью по ходу движения: для каждого края контура, опускающегося вниз, area += x1+x2. Для каждого края контура, идущего вверх, area -= x1+x2. В конце area = abs(area)/2.

Запомните площадь самой большой фигуры.

Ответ принят как подходящий

Ваша интуиция по формированию выпуклой оболочки для каждого кластера точек — хорошая идея. Однако, поскольку максимальное расстояние сегментов линии равно единице, нам придется изменить алгоритм выпуклой оболочки, поскольку не гарантируется, что все внутренние углы будут меньше 180 градусов с этим дополнительным ограничением при формировании оболочки.

Вот одно из возможных решений.

Мы можем модифицировать сканирование Грэма, заменив сортировку упорядоченным обходом пути. Путь должен состоять из ребер длины один между любыми двумя точками и быть круговым.

Предполагая, что нет перекрывающихся фигур, мы можем найти самый длинный соединенный круговой путь для каждой точки. Мы начинаем с построения списка смежности для представления графа со смещением против часовой стрелки, где каждая точка соединена с соседними точками на расстоянии одной единицы. Затем мы выполняем поиск в глубину из каждой точки, чтобы изучить все возможные пути. Вот одна из таких реализаций:

def dfs(point, graph, path, visited, start_point, longest_path, global_visited):
    path.append(point)
    visited.add(point)

    for neighbor in graph[point]:
        if neighbor == start_point and len(path) > 2:
            if len(path) > len(longest_path[0]):
                longest_path[0] = path.copy()
        elif neighbor not in visited and neighbor not in global_visited:
            dfs(neighbor, graph, path, visited, start_point, longest_path, global_visited)

    path.pop()
    visited.remove(point)

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

Для этих points взято из ответа Timeless.

 points = [
    (4, 11), (5, 10), (5, 11), (5, 12), (6, 10),
    (6, 11), (8, 12), (11, 11), (12, 10), (12, 12),
    (13, 10), (13, 11), (16, 9), (17, 7), (17, 8),
    (17, 10), (18, 8), (18, 9), (19, 10), (21, 9),
    (21, 10), (21, 11), (22, 10), (22, 11),
 ]

Мы находим следующие пути.

Затем мы можем изменить алгоритм сканирования Грэма, чтобы включить ограничение смежности, гарантируя, что сегменты между точками действительны. Это справедливо, поскольку путь, по которому следует следовать, смещен против часовой стрелки, следовательно, cross > 0.

def is_valid_segment(p1, p2):
     return (abs(p1[0] - p2[0]) == 1 and abs(p1[1] - p2[1]) == 0) or \
           (abs(p1[0] - p2[0]) == 0 and abs(p1[1] - p2[1]) == 1) or \
           (abs(p1[0] - p2[0]) == 1 and abs(p1[1] - p2[1]) == 1)

def cross(o, a, b):
    return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])

def build_hull(points):
    hull = []
    for p in points:
        while len(hull) >= 2 and cross(hull[-2], hull[-1], p) > 0:
            if is_valid_segment(hull[-2], p):
                hull.pop()
            else:
                 break
        hull.append(p)
    return hull

Мы можем передать найденные пути в модифицированное сканирование Грэма, чтобы сформировать оболочки.

Вот полный код. Это всего лишь черновик, и есть возможности для оптимизации.


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

, чтобы найти наибольшую форму.

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

Andrey Varvaryuk 17.06.2024 09:45

Другие вопросы по теме