Алгоритм для перечисления всех возможных способов прямоугольника можно разбить на n меньших прямоугольников
Смотрите название вопроса. Единственным другим ограничением является то, что меньшие прямоугольники должны быть сформированы путем погружения больших прямоугольников пополам. Я приложил результат для n = 3 и n = 4 ниже. Надеюсь, этого будет достаточно, чтобы объяснить смысл моих вопросов.
В настоящее время у меня есть неэффективный рекурсивный алгоритм, который делит каждый прямоугольник по горизонтали и вертикали и отслеживает все возможные комбинации в массиве. Мне не нравится этот алгоритм. Это полиномиальное время, кажется излишне сложным и дает мне дубликаты, как видно на рисунке n = 4 (подсказка: ищите четыре равных квадранта)
Мне было интересно, может ли быть лучшее решение для этого? Я экспериментировал с использованием 4-арного дерева (где каждый ребенок получает вертикальный или горизонтальный кусок), и я могу построить дерево, но получение всех возможных комбинаций из дерева, похоже, ускользает от меня. Я выложу код дерева котельной плиты ниже:
class Node:
#value is an tuple (x0,y0,x1,y1)
def __init__(self, value):
self.horizontal = []
self.vertical = []
self.value = value
def createTree(depth, currDepth, node):
if currDepth == depth:
return
node.horizontal.append(Node(getLeftRect(node.value)))
node.horizontal.append(Node(getRightRect(node.value)))
node.vertical.append(Node(getTopRect(node.value)))
node.vertical.append(Node(getBotRect(node.value)))
createTree(depth, currDepth+1, node.horizontal[0])
createTree(depth, currDepth+1, node.horizontal[1])
createTree(depth, currDepth+1, node.vertical[0])
createTree(depth, currDepth+1, node.vertical[1])
Любые предложения/помощь приветствуется!
Примечание: это не курсовая работа. Я пытаюсь создать пользовательский интерфейс для настраиваемого инструмента виртуального монитора, над которым я работаю.
![enter image description here]()
![n=4]()
Ответы
Ответ 1
Одна стратегия заключается в том, что, когда мы разрезаем по вертикали, не позволяйте обеим левым и правым частям горизонтального разреза. Это включает в себя анализ некоторых случаев.
В Python 3 мы сначала имеем типы данных для представления разделенных прямоугольников.
import collections
H = collections.namedtuple('H', ('top', 'bottom')) # horizontal cut
V = collections.namedtuple('V', ('left', 'right')) # vertical cut
W = collections.namedtuple('W', ()) # whole rectangle
Вот генераторы.
def generate(n):
assert isinstance(n, int) and n >= 0
yield from generate_with_horizontal(n)
yield from generate_without_horizontal(n)
def generate_with_horizontal(n):
assert isinstance(n, int) and n >= 0
for k in range(n):
for top in generate(k):
for bottom in generate(n - 1 - k):
yield H(top, bottom)
def generate_without_horizontal(n):
assert isinstance(n, int) and n >= 0
if n == 0:
yield W()
for k in range(n):
for left in generate_with_horizontal(k):
for right in generate_without_horizontal(n - 1 - k):
yield V(left, right)
for left in generate_without_horizontal(k):
for right in generate(n - 1 - k):
yield V(left, right)
Ответ 2
Идея для рекурсивного или древовидного алгоритма, который позволяет избежать дубликатов:
Вы начинаете с прямоугольника, и несколько раз его нужно разделить. Разделите его в обоих направлениях и уменьшите число на единицу, а затем на каждое разделение (вертикальное и горизонтальное) разделите номер на две части.
![прямоугольник divider]()
Этот метод приводит к 39 делениям при делении на 4 части (и 1 дубликат).
Единственным дубликатом, которого я не смог избежать, является крест. Используя этот метод, всякий раз, когда у вас есть прямоугольник, который нужно разделить еще на 3 или более раз, вы будете дважды сталкиваться с крестом. Поэтому вам придется добавить дополнительную проверку.
Вы также заметите, что 4 группы из 8 растворов, возникающих в результате начального разбиения 2,0
, являются поворотами друг друга на 90 °, 180 ° и 270 °. И 2 группы из 4 растворов, возникающих в результате начального разбиения 1,1
, являются поворотными на 90 ° друг от друга. Таким образом, вы можете решить только одну группу, а затем повернуть, чтобы получить все решения.
Кажется, что дубликаты будут сложнее избежать с помощью этого метода, чем я думал. Если вы добавите еще 2 раздела, кажущиеся очень разными L3 R1
и T2 B2
основные параметры приводят к нескольким дубликатам. 4 шага дальше:
![дублирование дуги прямоугольника]()
Как упоминает Дэвид Эйзенстат в своем ответе, вы можете избежать перекрестных двойников, разрешив обе половины прямоугольника делиться в один порядок (например, сначала вертикальный, затем горизонтальный), но не другой. Это означает, что при обработке прямоугольника вы должны знать, где находится его "другая половина", и как и как эта половина была разделена; что усложняет код, необходимый для использования этого метода.
Ответ 3
Здесь рекурсия в Python, которая сохраняет дерево в качестве словаря, где дети индексируются как 2i
и 2i + 1
. Я попытался реализовать предложение Дэвида Эйзенстата об избежании горизонтального раскола по обе стороны вертикального раскола (количество результатов, похоже, соответствует тем, которые он представил в комментариях).
from sets import Set
def f(n):
results = []
def _f(n,result,indexes):
if n == 1:
results.append(result)
return
for i in list(indexes):
indexes.remove(i)
parent = i // 2
sibling = i - 1 if i & 1 else i + 1
left = 2 * i
right = 2 * i + 1
# add horizontal split
if not (False if i < 2 else result[sibling] == 'H' and result[parent] == 'V'):
result_h = result.copy()
indexes_h = indexes.copy()
result_h[i] = 'H'
result_h[left] = result_h[right] = 'W'
indexes_h.add(left)
indexes_h.add(right)
_f(n - 1, result_h, indexes_h)
# add vertical split
result_v = result.copy()
indexes_v = indexes.copy()
result_v[i] = 'V'
result_v[left] = result_v[right] = 'W'
indexes_v.add(left)
indexes_v.add(right)
_f(n - 1, result_v, indexes_v)
_f(n,{1:1},Set([1]))
return results
Результаты для f(4)
:
{1: 'H', 2: 'H', 3: 'H', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'H', 2: 'H', 3: 'V', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'H', 2: 'H', 3: 'W', 4: 'H', 5: 'W', 8: 'W', 9: 'W'}
{1: 'H', 2: 'H', 3: 'W', 4: 'V', 5: 'W', 8: 'W', 9: 'W'}
{1: 'H', 2: 'H', 3: 'W', 4: 'W', 5: 'H', 10: 'W', 11: 'W'}
{1: 'H', 2: 'H', 3: 'W', 4: 'W', 5: 'V', 10: 'W', 11: 'W'}
{1: 'H', 2: 'V', 3: 'H', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'H', 2: 'V', 3: 'V', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'H', 2: 'V', 3: 'W', 4: 'H', 5: 'W', 8: 'W', 9: 'W'}
{1: 'H', 2: 'V', 3: 'W', 4: 'V', 5: 'W', 8: 'W', 9: 'W'}
{1: 'H', 2: 'V', 3: 'W', 4: 'W', 5: 'H', 10: 'W', 11: 'W'}
{1: 'H', 2: 'V', 3: 'W', 4: 'W', 5: 'V', 10: 'W', 11: 'W'}
{1: 'H', 2: 'W', 3: 'H', 6: 'H', 7: 'W', 12: 'W', 13: 'W'}
{1: 'H', 2: 'W', 3: 'H', 6: 'V', 7: 'W', 12: 'W', 13: 'W'}
{1: 'H', 2: 'W', 3: 'H', 6: 'W', 7: 'H', 14: 'W', 15: 'W'}
{1: 'H', 2: 'W', 3: 'H', 6: 'W', 7: 'V', 14: 'W', 15: 'W'}
{1: 'H', 2: 'W', 3: 'V', 6: 'H', 7: 'W', 12: 'W', 13: 'W'}
{1: 'H', 2: 'W', 3: 'V', 6: 'V', 7: 'W', 12: 'W', 13: 'W'}
{1: 'H', 2: 'W', 3: 'V', 6: 'W', 7: 'H', 14: 'W', 15: 'W'}
{1: 'H', 2: 'W', 3: 'V', 6: 'W', 7: 'V', 14: 'W', 15: 'W'}
{1: 'V', 2: 'H', 3: 'V', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'V', 2: 'H', 3: 'W', 4: 'H', 5: 'W', 8: 'W', 9: 'W'}
{1: 'V', 2: 'H', 3: 'W', 4: 'V', 5: 'W', 8: 'W', 9: 'W'}
{1: 'V', 2: 'H', 3: 'W', 4: 'W', 5: 'H', 10: 'W', 11: 'W'}
{1: 'V', 2: 'H', 3: 'W', 4: 'W', 5: 'V', 10: 'W', 11: 'W'}
{1: 'V', 2: 'V', 3: 'H', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'V', 2: 'V', 3: 'V', 4: 'W', 5: 'W', 6: 'W', 7: 'W'}
{1: 'V', 2: 'V', 3: 'W', 4: 'H', 5: 'W', 8: 'W', 9: 'W'}
{1: 'V', 2: 'V', 3: 'W', 4: 'V', 5: 'W', 8: 'W', 9: 'W'}
{1: 'V', 2: 'V', 3: 'W', 4: 'W', 5: 'H', 10: 'W', 11: 'W'}
{1: 'V', 2: 'V', 3: 'W', 4: 'W', 5: 'V', 10: 'W', 11: 'W'}
{1: 'V', 2: 'W', 3: 'H', 6: 'H', 7: 'W', 12: 'W', 13: 'W'}
{1: 'V', 2: 'W', 3: 'H', 6: 'V', 7: 'W', 12: 'W', 13: 'W'}
{1: 'V', 2: 'W', 3: 'H', 6: 'W', 7: 'H', 14: 'W', 15: 'W'}
{1: 'V', 2: 'W', 3: 'H', 6: 'W', 7: 'V', 14: 'W', 15: 'W'}
{1: 'V', 2: 'W', 3: 'V', 6: 'H', 7: 'W', 12: 'W', 13: 'W'}
{1: 'V', 2: 'W', 3: 'V', 6: 'V', 7: 'W', 12: 'W', 13: 'W'}
{1: 'V', 2: 'W', 3: 'V', 6: 'W', 7: 'H', 14: 'W', 15: 'W'}
{1: 'V', 2: 'W', 3: 'V', 6: 'W', 7: 'V', 14: 'W', 15: 'W'}
Ответ 4
Итак, похоже, что существует несколько способов генерации всех неизоморфных возможностей, поэтому мое предложение было бы:
- Сгенерировать их для некоторого количества мониторов.
- Удалить дубликаты и сохранить результаты.
- Когда вам это нужно, прочитайте из файла.
Дело в том, что если вам не нужен результат для больших входов (скажем, 6-10), вы не хотите генерировать их на лету. Даже если вы хотите показать результаты для такого размера раздела, это, безусловно, будет намного больше, чем вы могли бы с пользой показать пользователю!
Тем не менее, если вы хотите генерировать неизоморфные представители этих структур, есть интересное исследование "разрезанных двойников" - см., например, this магистерская диссертация Винсента Кустерса. Обратите внимание, что ваши структуры более общие, поскольку они включают разделы, которые встречаются при объединении с четырьмя.
Ответ 5
Это не прямой ответ на ваш вопрос, но вы должны рассмотреть его:
Будьте очень осторожны при использовании рекурсивного алгоритма для подсчета количества разделов при n> 4. Причина в том, что у нас может быть несколько разделов, в которых слияние NO двух прямоугольников заканчивается в другом прямоугольнике. Например, для пяти разделов рассмотрим следующее:
Картина здесь
Я думаю, что алгоритмы, предложенные выше, пропускают все разделы, подобные этому.