반복적으로 등장하지만 자주 실수하는 문제 중 하나가 “두 구간이 겹치는가?”입니다. 핵심은 반열림 규약과 부정(negation)입니다. 반열림 \([start, end)\)을 채택하면 오프바이원을 크게 줄일 수 있고, “겹친다”를 직접 나열하기보다는 “겹치지 않는다”의 부정을 통해 더 단순한 판별식을 얻을 수 있습니다. 좋은 정리는 간단한 술어(predicate)에서 시작합니다.
참고: 본 글의 구조적 아이디어와 예시는 “How to check for overlapping intervals"에서 제시한 부정 기반 접근을 바탕으로 재구성했습니다. 상세 설명과 도형은 원문을 참고하세요.
1차원 구간: 반열림 [start, end)
두 반열림 구간 \([aStart, aEnd), [bStart, bEnd)\)이 겹치는 충분조건·필요조건은 다음과 같이 깔끔합니다.
\[ a \text{와 } b \text{가 겹침} \iff bStart < aEnd \;\land\; aStart < bEnd \]이 식은 “겹치지 않는다”의 두 경우(\(a\)가 \(b\)보다 앞, 또는 뒤)를 부정해 얻습니다.
\[ \lnot(aEnd \le bStart \;\lor\; bEnd \le aStart) \equiv (bStart < aEnd) \land (aStart < bEnd) \]파이썬
|
|
자바스크립트/타입스크립트
|
|
C++
|
|
교집합 기반 동일식
교집합을 직접 만들면 직관이 더욱 분명해집니다. 반열림 규약에서는 다음이 동치입니다.
\[ [\max(aStart,bStart),\; \min(aEnd,bEnd)) \text{가 비어있지 않다} \iff \max < \min \]
|
|
2차원 직사각형(AABB): 축 정렬 상자 겹침
축에 정렬된 두 상자 \(A = [Ax1, Ax2) \times [Ay1, Ay2), B = [Bx1, Bx2) \times [By1, By2)\)는 두 축 모두에서 1차원 겹침이 성립해야 겹칩니다.
\[ Bx1 < Ax2 \land Ax1 < Bx2 \land By1 < Ay2 \land Ay1 < By2 \]
|
|
위 식은 1차원 판별의 곱(AND)입니다. 한 축이라도 겹치지 않으면 상자도 겹치지 않습니다.
실무 함정과 체크리스트
- 오프바이원: 정수 인덱스, 날짜 범위에서는 반열림 \([start, end)\)로 표현하면 경계 처리가 단순해집니다. 예: 하루 단위는 \([2025-01-01, 2025-01-02)\).
- 폐구간 혼용: 양쪽이 모두 폐구간이면
<=
로 바뀝니다. 혼합 규약([start, end), [start, end])은 피하세요. - 시간/타임존: 날짜·시간은 타임존, 서머타임 전환에 주의하세요. 가급적 UTC 타임스탬프(정수 ms/초)로 변환 후 반열림 규약을 적용합니다. 참고: Baeldung — Check if Two Date Ranges Overlap
- 부동소수점: 경계가 부동소수점이면 정수 스케일(예: 센티초)로 변환하거나, 엄격 비교에 작은 여유(ε)를 도입합니다.
- 비정상 구간: 입력 검증으로
start <= end
(반열림에서 빈 구간 허용 시start == end
)을 보장하세요. - 성질 테스트: 대칭성, 교환 법칙(인자 순서 바뀌어도 결과 동일), 빈 교집합 판정과의 동치 등을 단위테스트에 포함합니다.
대량 데이터에서의 응용
- 선 스윕(sweep line): 많은 구간의 모든 겹침을 찾을 때는 시작·끝을 정렬해 선 스윕으로 \(O(n \log n + k)\)에 처리합니다.
- 인덱스 구조: 동적 질의·갱신에는 인터벌 트리, 세그먼트 트리, 구간 트리를 검토하세요.
요약
- 반열림 \([start, end)\)을 채택하면 경계 오류가 줄어듭니다.
- “겹치지 않는다”를 부정해 얻은 식이 가장 단순합니다:
bStart < aEnd && aStart < bEnd
. - 2D는 축별 겹침의 AND입니다. 실제 시스템에서는 시간대·부동소수점·데이터 정합성 검증까지 포함해 방어적으로 구현하세요.