【问题标题】:Split a rectangle into n equally sized rectangles将一个矩形分割成 n 个大小相等的矩形
【发布时间】:2011-08-01 11:32:06
【问题描述】:

我正在寻找一种算法,将一个矩形(比如说 1000x800)分割成 n 个(或更多,但尽可能少的额外矩形)在该矩形内大小相等的矩形,因此所有空间都被使用。小矩形也应尽量接近原始纵横比。

例如:

+---------------+
|               |
|               |
|               |
+---------------+

拆分为 n = 2:

+---------------+
|               |
+---------------+
|               |
+---------------+

拆分为 n = 3

+-------+-------+
|       |       |
+-------+-------+
|       |       |
+---------------+

等等

有这样的算法吗?理想情况下,我希望用 Python 编写它,但实际上任何语言都可以,因为我应该能够翻译它......

编辑:

一些额外的信息:

目标表面将是一个浏览器窗口,因此表面将大致为 4:3 或 16:9 或其他流行尺寸。矩形由像素组成。纵横比保证为整数。

与纵横比限制相比,较少的多余矩形稍微可取。

【问题讨论】:

  • 你应该为下一个接受率最接近的矩形选择min(a, b), max(a, b)/2 对。顺便说一句,维度是整数吗?
  • 您有两个约束条件“尽可能少的额外矩形”和“尽可能接近原始纵横比”——其中哪个更重要? (从 n=3 的示例中,它使用 4 个矩形,可能是第二个。)您如何衡量与原始纵横比的“接近度”?
  • 诸如“大致”和“略好”之类的描述不是很有用,除非您可以使其精确。 :-)(例如,对于 n=3,您可以将水平或垂直分割成正好 3 个矩形;确切地说,为什么这不是首选?)
  • 嗯,大致是因为我不知道人们会在什么分辨率下使用这个软件。只是因为较少的多余矩形不应该总是胜过纵横比。
  • @ojii:那么它应该在什么时候胜出呢? (无论如何,Gareth 在下面试图适应任意评价函数的答案可能是没有读心术的最佳答案。)

标签: python algorithm geometry


【解决方案1】:

(我假设,也许是错误的,你的矩形是无限可分的,而不是由离散的像素组成。)

通过让 m = ceil(sqrt(n)) 并在每边使用 m 个片段,您总是可以在浪费的矩形中以一定的成本获得准确的纵横比。

否则,您正在寻找接近 sqrt(n) 的 p,q 使得 pq >= n 和 p,q 彼此接近。 p,q 的最佳选择当然取决于您在浪费与不准确之间进行权衡的意愿。您似乎不太可能希望将 p,q 与 sqrt(n) 相距甚远,因为这样做会给您的形状带来很大的错误。所以我认为你想要这样的东西:

p = ceiling(sqrt(n))
best_merit_yet = merit_function(p,p,0)
best_configuration_yet = (p,p)
for p from floor(sqrt(n)) downward:
  # we need pq >= n and q as near to p as possible, which means (since p is too small) as small as possible
  q = ceiling(n/p)
  if max(merit_function(n/p,n/q,0), merit_function(n/q,n/p,0)) < best_merit_yet:
    break
  n_wasted = p*q-n
  merit1 = merit_function(n/p,n/q,n_wasted)
  merit2 = merit_function(n/q,n/p,n_wasted)
  if max(merit1,merit2) > best_merit_yet:
    if merit1 > merit2:
      best_configuration_yet = (p,q)
      best_merit_yet = merit1
    else:
      best_configuration_yet = (q,p)
      best_merit_yet = merit2

希望非常错误的形状非常糟糕这一事实意味着您实际上不必进行多次循环迭代。

在这里,merit_function 应该体现您对形状与浪费进行权衡的偏好。

【讨论】:

    【解决方案2】:

    使用此函数获取 2 个数字作为列表:

    def divide_equally(n):
        if (n<3):
            return [n, 1]
        result = list()
        for i in range(1, int(n ** 0.5) + 1):
           div, mod = divmod(n, i)
           #ignore 1 and n itself as factors
           if mod == 0 and i != 1 and div != n:
               result.append(div)
               result.append(i)
        if len(result)==0: # if no factors then add 1
            return divide_equally(n+1)
        return result[len(result)-2:]
    

    例如:

    print divide_equally(1)
    print divide_equally(50)
    print divide_equally(99)
    print divide_equally(23)
    print divide_equally(50)
    

    会给

    [1, 1]
    [10, 5]
    [11, 9]
    [6, 4]  # use the next even number (24)
    [10, 5] # not [25, 2] use the 2 closest numbers 
    

    【讨论】:

      【解决方案3】:

      编辑:第二个想法:

      var countHor = Math.Floor(Math.Sqrt(n));
      var countVer = Math.Ceil(n / countHor);
      var widthDivided = widthTotal / countVer;
      var heightDivided = heightTotal / countHor;
      

      虽然结果取决于您更喜欢矩形比例还是额外的矩形数量(例如,对于 n = 14,应该是 2x7 还是 3x5,对于 n = 7,应该是 3x3 还是 2x4)

      第一个想法是错误的,由于一些消耗:

      如果你想获得最少数量的相等矩形,你应该使用 sqrt 操作。例如,如果 n = 9,那么它将是 3x3 矩形(垂直 2 行,水平 2 行)。如果 n = 10,它将是 3x4 矩形,如 floor(sqrt(10)) x ceil(sqrt(10)) => 3x4(垂直 2 行,水平 3 行或其他)。

      这只是一般的算法思想,根据您的要求,您应该构建正确的算法。

      新矩形的大小如下:

      var widthDivided = widthTotal / Math.Floor(Math.Sqrt(count));
      var heightDivided = heightTotal / Math.Ceil(Math.Sqrt(count));
      

      这是类似的任务,但它不会返回最小值: Algorithm to split rectangle into n smaller rectangles and calculate each center

      【讨论】:

      • 对于n=15,您的算法给出3x4=12 rects,您可以通过2 rows7 cols 获得14
      • @pajton 是的,我假设了一些事实来简化我最初的想法,这是错误的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      相关资源
      最近更新 更多