배치와 연결 (2) - 가장 작은 면적을 갖도록 배치하기


가장 작은 면적을 갖도록 배치하기

이전 페이지에서 특정 평면에서 빈 공간을 찾아 사각형 상자를 배치하는 방법에 대해 소개했습니다.

이번에는 네 개의 상자가 작은 면적을 차지하도록 하는 배치를 찾는 과정에 대해 소개합니다.




1) 모듈 임포트 & 함수 정의하기

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches


def draw_rect(canvas, rectangle):
    (x, y, w, h) = rectangle
    canvas[y:y+h, x:x+w] = len(rect_info)


def find_place(canvas, rectangle):
    w_rect, h_rect = rectangle[2], rectangle[3]
    options = []
    for i in range(height - h_rect + 1):
        for j in range(width - w_rect + 1):
            if not np.any(canvas[i:i+h_rect, j:j+w_rect]):
                options.append((j, i))
    rand = np.random.randint(len(options))
    return options[rand]

draw_rect(canvas, rectangle) 함수는 2차원 어레이 canvas에 사각형 rectangle을 배치합니다.

find_place(canvas, rectangle) 함수는 2차원 어레이 canvas에 사각형 rectangle을 놓을 수 있는 위치를 찾아서 반환합니다.




2) 변수 지정하기

width, height = 10, 10
new_rect= [[0, 0, 4, 4], [0, 0, 3, 4], [0, 0, 4, 2], [0, 0, 3, 2]]
record_x, record_y = 10, 10
record_rect_info = []
num_episodes = 1000

width, height는 평면 공간의 너비와 높이이고, new_rect는 이 공간에 놓여질 사각형의 (x, y, w, h) 정보입니다.

record_x, record_y는 가장 작은 x, y 위치를 저장합니다.

record_rect_info는 가장 작은 면적을 갖는 사각형 정보를 갖는 리스트입니다.

num_episodes회 동안 네 개의 사각형을 임의로 배치해서 가장 작은 면적을 갖는 위치를 저장하게 됩니다.




3) 가장 작은 면적을 갖는 배치 찾기

for n in range(num_episodes):
  canvas1 = np.zeros((height, width))
  rect_info = []
  for rect in new_rect:
      rect[0], rect[1] = find_place(canvas1, rect)
      rect_info.append(tuple(rect))
      draw_rect(canvas1, rect)

  max_x = max(np.array(rect_info)[:, 0] + np.array(rect_info)[:, 2])
  max_y = max(np.array(rect_info)[:, 1] + np.array(rect_info)[:, 3])
  if max_x <= record_x and max_y <= record_y:
      record_rect_info = rect_info
      record_x, record_y = max_x, max_y

1000회의 에피소드 동안 빈 자리를 찾아 임의로 사각형을 배치하고, x, y 위치의 최대값이 가장 작은 배치인 경우

이 배치를 record_rect_info에 저장합니다.




4) Matplotlib 시각화하기

plt.style.use('default')
plt.rcParams['figure.figsize'] = (6, 6)
plt.rcParams['font.size'] = 12

fig, ax = plt.subplots()
for (x, y, w, h) in record_rect_info:
    ax.add_patch(
        patches.Rectangle(
            (x, y), w, h,
            edgecolor='royalblue',
            facecolor='lightsteelblue',
            linewidth=0.5,
            fill=True))

ax.axis((0, 10, 10, 0))
ax.xaxis.tick_top()
plt.show()

위 코드는 네 개의 상자들의 정보를 갖는 리스트 record_rect_info를 Matplotlib를 사용해서 시각화합니다.

Matplotlib patches 모듈을 사용해서 도형 시각화하기 페이지를 참고하세요.

아래 그림은 num_episodes를 1000회로 했을 때, 세 번의 결과를 나타냅니다.


가장 작은 면적을 갖도록 배치하기



아래 그림은 num_episodes를 30000회로 했을 때, 세 번의 결과를 나타냅니다.


가장 작은 면적을 갖도록 배치하기




전체 코드는 아래와 같습니다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches


def draw_rect(canvas, rectangle):
  (x, y, w, h) = rectangle
  canvas[y:y+h, x:x+w] = len(rect_info)


def find_place(canvas, rectangle):
  w_rect, h_rect = rectangle[2], rectangle[3]
  options = []
  for i in range(height - h_rect + 1):
      for j in range(width - w_rect + 1):
          if not np.any(canvas[i:i+h_rect, j:j+w_rect]):
              options.append((j, i))
  rand = np.random.randint(len(options))
  return options[rand]


width, height = 10, 10
new_rect= [[0, 0, 4, 4], [0, 0, 3, 4], [0, 0, 4, 2], [0, 0, 3, 2]]
record_x, record_y = 10, 10
record_rect_info = []
num_episodes = 1000

for n in range(num_episodes):
  canvas1 = np.zeros((height, width))
  rect_info = []
  for rect in new_rect:
      rect[0], rect[1] = find_place(canvas1, rect)
      rect_info.append(tuple(rect))
      draw_rect(canvas1, rect)

  max_x = max(np.array(rect_info)[:, 0] + np.array(rect_info)[:, 2])
  max_y = max(np.array(rect_info)[:, 1] + np.array(rect_info)[:, 3])
  if max_x <= record_x and max_y <= record_y:
      record_rect_info = rect_info
      record_x, record_y = max_x, max_y


plt.style.use('default')
plt.rcParams['figure.figsize'] = (6, 6)
plt.rcParams['font.size'] = 12

fig, ax = plt.subplots()
for (x, y, w, h) in record_rect_info:
    ax.add_patch(
        patches.Rectangle(
            (x, y), w, h,
            edgecolor='royalblue',
            facecolor='lightsteelblue',
            linewidth=0.5,
            fill=True))

ax.axis((0, 10, 10, 0))
ax.xaxis.tick_top()
plt.show()


이전글/다음글