Search

OR Tools 이용해서 2D Bin Packing Problem 풀어보기

2D Bin Packing 문제는 제한된 크기의 2차원 공간(빈)에 주어진 일련의 직사각형 물체들을 가능한 한 효율적으로 배치하는 문제입니다.
목표는 주어진 물체를 모두 빈에 담되, 최소한의 빈을 사용하는 것입니다.
즉, 가능한 한 많은 물체를 한 상자에 담거나, 필요한 상자의 개수를 최소화하는 것이 핵심입니다.
이 문제는 물체의 개수가 많아질수록 가능한 배치의 수가 기하급수적으로 증가하게 됩니다.
이로 인해 최적 해를 찾는 것은 매우 어려워질 수 있으며, 현실적으로는 문제의 복잡성을 고려해
휴리스틱(Heuristic) 알고리즘을 사용하여 합리적인 시간 내에 해결책을 찾는 것이 일반적입니다.
문제 정의 1. 물체는 빈의 경계를 넘을 수 없습니다. 2. 물체들끼리 서로 겹치지 않아야 합니다. 3. 빈의 크기는 고정되어 있으며, 물체의 크기는 사전에 정의됩니다.
Initialization
__init__ 함수는 상자의 너비(bin_width), 높이(bin_height), 그리고 물품들의 크기(items)를 입력으로 받아 모델을 초기화합니다. items는 각 물품의 (너비, 높이)로 구성된 리스트입니다.
Decision Variable
각 물품의 좌표를 나타내는 변수 xy를 생성합니다. 이는 각각 물품의 왼쪽 아래 모서리가 상자의 왼쪽 아래 모서리에서부터 얼마나 떨어져 있는지를 나타냅니다.
Constraints
물품들이 서로 겹치지 않도록 제약 조건을 설정합니다. 이를 위해 각 물품 쌍에 대해 네 가지 조건을 만듭니다:
1.
물품 i가 물품 j의 오른쪽에 있는 경우.
2.
물품 i가 물품 j의 왼쪽에 있는 경우.
3.
물품 i가 물품 j의 위쪽에 있는 경우.
4.
물품 i가 물품 j의 아래쪽에 있는 경우.
네 가지 조건 중 하나는 반드시 성립해야 하므로 AddBoolOr 함수를 사용해 물품들이 서로 겹치지 않도록 제약 설정 합니다.
전체소스
from ortools.sat.python import cp_model class BinPacking2D: def __init__(self, bin_width, bin_height, items): self.bin_width = bin_width self.bin_height = bin_height self.items = items self.num_items = len(items) self.model = cp_model.CpModel() def solve(self): # Variables x = [self.model.NewIntVar(0, self.bin_width - item[0], f'x_{i}') for i, item in enumerate(self.items)] y = [self.model.NewIntVar(0, self.bin_height - item[1], f'y_{i}') for i, item in enumerate(self.items)] bin_used = self.model.NewBoolVar('bin_used') # Constraints for no overlap between items for i in range(self.num_items): for j in range(i + 1, self.num_items): # Create boolean variables for each non-overlapping condition no_overlap_right = self.model.NewBoolVar(f'no_overlap_right_{i}_{j}') no_overlap_left = self.model.NewBoolVar(f'no_overlap_left_{i}_{j}') no_overlap_above = self.model.NewBoolVar(f'no_overlap_above_{i}_{j}') no_overlap_below = self.model.NewBoolVar(f'no_overlap_below_{i}_{j}') # item i is to the right of item j self.model.Add(x[i] + self.items[i][0] <= x[j]).OnlyEnforceIf(no_overlap_right) # item i is to the left of item j self.model.Add(x[j] + self.items[j][0] <= x[i]).OnlyEnforceIf(no_overlap_left) # item i is above item j self.model.Add(y[i] + self.items[i][1] <= y[j]).OnlyEnforceIf(no_overlap_above) # item i is below item j self.model.Add(y[j] + self.items[j][1] <= y[i]).OnlyEnforceIf(no_overlap_below) # At least one of these conditions must hold (no overlap between i and j) self.model.AddBoolOr([no_overlap_right, no_overlap_left, no_overlap_above, no_overlap_below]) # All items must fit within the bin dimensions for i in range(self.num_items): self.model.Add(x[i] + self.items[i][0] <= self.bin_width) self.model.Add(y[i] + self.items[i][1] <= self.bin_height) # Objective: Minimize the number of bins used (in a more complex version, but not required for this demo) self.model.Minimize(bin_used) # Solve the model solver = cp_model.CpSolver() status = solver.Solve(self.model) if status == cp_model.OPTIMAL: print('Optimal solution found!') for i in range(self.num_items): print(f'Item {i}: x={solver.Value(x[i])}, y={solver.Value(y[i])}') else: print('No solution found.') # Example usage bin_width = 10 bin_height = 10 items = [(3, 3), (5, 2), (4, 4), (1, 9), (2, 3), (2, 3), (3, 3), (3, 3), (3,3), (10, 1)] # List of items with their width and height bin_packing = BinPacking2D(bin_width, bin_height, items) bin_packing.solve()
Python
복사
결과를 시각화 하면 아래와 같습니다.
Item 0: x=7, y=0 Item 1: x=2, y=3 Item 2: x=1, y=5 Item 3: x=0, y=0 Item 4: x=5, y=0 Item 5: x=5, y=5 Item 6: x=7, y=3 Item 7: x=7, y=6 Item 8: x=1, y=0 Item 9: x=0, y=9
Plain Text
복사
Keyword: Logistics Optimization, 물류 최적화, Linear Programming, Mixed Integer Programming, Google OR Tools
Reference:
[1] The Bin Packing Problem by Google, https://developers.google.com/optimization/pack/bin_packing, Creative Commons Attribution 4.0 License