用于枚举矩形可以分成n个较小矩形的所有可能方式的算法

时间:2016-06-12 01:31:51

标签: algorithm optimization

查看问题标题。唯一的另一个限制是较小的矩形必须通过将较大的矩形潜入一半而形成。我已将结果附加到n = 3和n = 4以下。希望这足以解释我的问题的含义。

目前,我有一个低效的递归算法,可以水平和垂直地划分每个矩形,并跟踪数组中所有可能的组合。我不喜欢这个算法。它是多项式时间,似乎不必要地复杂并给我重复,如n = 4图片中所示(提示:寻找四个相等的象限)

我想知道是否有更好的解决方案呢?我正在尝试使用4-ary树(每个孩子得到一个垂直或水平的部分),并且能够构建树,但从树中获得所有可能的组合似乎是在逃避我。我将在下面发布我的树木锅炉板代码:

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

5 个答案:

答案 0 :(得分:6)

一种策略是,当我们垂直切割时,不要让左半部分和右半部分都有水平切割。这涉及一些案例分析。

在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)

答案 1 :(得分:5)

避免重复的递归或树构建算法的想法:
你从一个矩形开始,并且必须多次分割。将其划分为两个方向,并将数字减少一个,然后对于每个分区(垂直和水平),将数字划分为两个部分。

rectangle divider

当分成4份(和1份重复)时,该方法产生39次分裂。

我唯一无法避免的重复是十字架。使用这种方法,每当你有一个需要再分割3次或更多次的矩形时,你就会遇到两次交叉。因此,您必须为此添加一些额外的检查。

您还会注意到,由初始2,0分割产生的4组8个解决方案彼此旋转90°,180°和270°。并且由初始1,1分割产生的2组4个解是彼此90°旋转。因此,您只能解决一个组,然后轮换以获取所有解决方案。

使用这种方法似乎比我最初想的更难以避免重复。如果再添加2个分部,看似非常不同的L3 R1T2 B2主要选项会导致几个重复的步骤:

rectangle division duplicates

正如David Eisenstat在他的回答中提到的那样,你可以避免交叉双打,只允许将矩形的两半分成一个顺序(例如,第一个垂直,然后是水平),而不是另一个顺序。这意味着在处理矩形时,你必须知道它的“另一半”在哪里,以及是否以及如何分割这一半;这使得使用此方法所需的代码变得复杂。

答案 2 :(得分:2)

这是Python中的递归,它将树保存为字典,其中子项被编入索引为2i2i + 1。我试图实现David Eisenstat关于避免垂直分割两侧水平分割的建议(结果的数量似乎与他在评论中提供的结果一致)。

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'}

答案 3 :(得分:0)

好的,所以看起来有几种方法可以产生所有非同构的可能性,所以我的建议是:

  1. 为一些监视器生成所有内容。
  2. 删除重复项并存储结果。
  3. 如果您需要,请从文件中读取。
  4. 问题是,除非您需要大输入(例如6-10)的结果,否则您不希望在运行中生成它们。即使您确实希望显示此分区大小的结果,它肯定会比您向用户显示的内容大得多!

    也就是说,如果你确实希望生成这些结构的非同构代表,那么有一些关于“可切片双重”的有趣研究 - 例如,见Vincent Kusters的this masters thesis。请注意,您的结构更为通用,因为它们包含在四连接处遇到的分区。

答案 4 :(得分:0)

这不是您问题的直接答案,但您应该考虑一下:

在使用递归算法计算n> 4的分区数时要非常小心。原因是我们可以有一些分区,其中没有两个矩形的末端合并成另一个矩形。例如,对于五个分区,请考虑以下内容:

The picture is here

我认为上面建议的算法会错过所有与此分区相似的分区。