簡體   English   中英

平鋪固定大小的矩形以覆蓋給定的一組點

[英]Tiling fixed-size rectangles to cover a given set of points

問題 - 給定平面點列表[p_1, ..., p_n]和一些矩形w, h的尺寸,找到覆蓋所有點的最小矩形w, h集(編輯 - 矩形不旋轉) .

我最初的解決方案是:

  • 找到所有點的邊界框
  • 將邊界框的寬度和高度除以給定矩形的w, h並將數字向上舍入以獲得xy矩形的實例數
  • 要進一步優化,請遍歷所有矩形並刪除其中包含零點的矩形。

Python中的一個例子:

def tile_rect(points, rect):
    w, h = rect
    xs = [p.x for p in points]
    ys = [p.y for p in points]
    bbox_w = abs(max(xs) - min(xs))
    bbox_h = abs(max(ys) - min(ys))
    n_x, n_y = ceil(bbox_w / w), ceil(bbox_h / h)
    rect_xs = [(min(xs) + n * w for n in range(n_x)]
    rect_ys = [(min(ys) + n * h for n in range(n_y)]
    rects = remove_empty(rect_xs, rect_ys)
    return rects

我怎樣才能做得更好? 我可以使用什么算法來減少矩形的數量?

為了離散整數規划的問題,觀察給定一個矩形,我們可以在 +x 和 +y 方向上滑動它,而不會減少覆蓋范圍,直到最小 x 和最小 y 線上都有一個點。 因此整數程序只是標准的最小覆蓋:

minimize sum_R x_R
subject to
for every point p, sum_{R contains p} x_R >= 1
x_R in {0, 1}

其中R涵蓋所有矩形,其最小 x 是某個點的 x,其最小 y 是某個點的 y(不一定是同一點)。

演示 Python:

import random
from ortools.linear_solver import pywraplp

w = 0.1
h = 0.1
points = [(random.random(), random.random()) for _ in range(100)]
rectangles = [(x, y) for (x, _) in points for (_, y) in points]
solver = pywraplp.Solver.CreateSolver("min cover", "SCIP")
objective = solver.Objective()
constraints = [solver.RowConstraint(1, pywraplp.inf, str(p)) for p in points]
variables = [solver.BoolVar(str(r)) for r in rectangles]
for (x, y), var in zip(rectangles, variables):
    objective.SetCoefficient(var, 1)
    for (px, py), con in zip(points, constraints):
        if x <= px <= x + w and y <= py <= y + h:
            con.SetCoefficient(var, 1)
solver.Objective().SetMinimization()
solver.Solve()

scale = 6 * 72
margin = 72
print(
    '<svg width="{}" height="{}">'.format(
        margin + scale + margin, margin + scale + margin
    )
)
print(
    '<text x="{}" y="{}">{} rectangles</text>'.format(
        margin // 2, margin // 2, round(objective.Value())
    )
)
for x, y in points:
    print(
        '<circle cx="{}" cy="{}" r="3" fill="none" stroke="black"/>'.format(
            margin + x * scale, margin + y * scale
        )
    )
for (x, y), var in zip(rectangles, variables):
    if var.solution_value():
        print(
            '<rect x="{}" y="{}" width="{}" height="{}" fill="none" stroke="rgb({},{},{})"/>'.format(
                margin + x * scale,
                margin + y * scale,
                w * scale,
                h * scale,
                random.randrange(192),
                random.randrange(192),
                random.randrange(192),
            )
        )
print("</svg>")

示例輸出:

在此處輸入圖片說明

假設一個近似的,而不是最佳的解決方案是可以接受的,那么一個程序通常像:

Until no points are left:
  (1) Find the convex hull of the remaining points.
  (2) Cover each point/s on the hull so the
      covering rectangles extend "inward."
      (Perhaps examine neighbouring hull points
      to see if a rectangle can cover more than one.)
  (3) Remove the newly covered points.

顯然,覆蓋矩形的方向對過程和結果有影響。 我認為有一種方法可以將 (1) 和 (3) 結合起來,或者可能依賴於嵌套的凸包,但我對這些沒有太多經驗。

這可以轉化為一個標准的集合覆蓋問題。 給定平面上的n個點,一般步驟如下。

  • 首先,生成所有可能的最大包含矩形,其中最多有 n^2 個,命名為 R。關鍵的見解是,給定坐標為 (x1, y1) 的點 p1,使用 x1 作為一組矩形的最左邊界. 對於具有 (x2,y2) 其中 x1 <= x2 <= x1+w 且 y1-h <= y2 <= y1+h 的所有其他點 p2,生成一個矩形 ((x1, y2), (x1+w, y2+h))。
  • 對於生成的每個矩形 r,計算包含在該矩形 cover(r) 中的點。
  • 選擇矩形 R, s 的一個子集,使得所有點都在 Union(r in s) cover(r) 中

現在,棘手的部分是最后一步。 幸運的是,這是一個標准問題,文獻中提出了許多算法。 例如,可以使用組合優化求解器(例如 SAT 求解器、MIP 求解器和約束編程求解器)。

請注意,上述重新公式僅適用於矩形可以相互覆蓋的情況。 可能是生成的矩形集不足以找到不重疊的最少矩形集。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM