본문으로 건너뛰기
김신건의 로그

선분 교차 (Line Segment Intersection)

· 수정 · 📖 약 3분 · 810자/단어 #algorithm #geometry #line-intersection
line intersection, 선분 교차, line segment intersection, CCW intersection, 선분 교차 판정

정의

선분 교차 판정 (Line Segment Intersection) 은 2D 평면 위 두 선분 AB 와 CD 가 서로 교차하는지 O(1) 에 판정하는 기하 연산. CCW (Counter-Clockwise) 벡터 외적만으로 이루어짐.

문제 상황과 동기

두 선분의 교차 여부는 기하 문제의 가장 기본적인 빌딩 블록.

  • naive: 모든 선분 쌍 O(N^2) 검사. N=10^3 이상에서 터짐.
  • O(1) CCW 판정: CCW 4 회로 교차 여부를 바로 알 수 있음.

핵심 통찰: 각 선분의 양 끝점이 상대 선분의 양 옆에 있는지 확인. 즉, CCW(A,B,C)CCW(A,B,D) 의 부호가 다른지, 동시에 CCW(C,D,A)CCW(C,D,B) 의 부호가 다른지.

활용: Convex Hull, 선분 그룹 (DSU), Sweeping 의 기초 연산.

시각화

핵심 아이디어

세 점의 CCW 값을 구하는 cross product:

ccw(a, b, c) = (b.x - a.x)(c.y - a.y) - (b.y - a.y)(c.x - a.x)
  • > 0 : 반시계 (좌회전)
  • < 0 : 시계 (우회전)
  • = 0 : 일직선 (collinear)

두 선분 AB, CD 가 교차하는 조건:

ccw(a,b,c) * ccw(a,b,d) <= 0  AND
ccw(c,d,a) * ccw(c,d,b) <= 0

즉, A-B 기준으로 C 와 D 가 서로 다른 쪽에 있고 (또는 하나가 선 위), C-D 기준으로 A 와 B 가 서로 다른 쪽에 있음.

Collinear (네 점이 모두 일직선) 인 경우는 projection range check 로 겹침 판정.

알고리즘

intersect(a, b, c, d):
    ab = ccw(a,b,c) * ccw(a,b,d)
    cd = ccw(c,d,a) * ccw(c,d,b)

    if ab == 0 and cd == 0:
        // 모두 일직선: x/y projection 이 겹치는지 확인
        if a.x > b.x: swap(a,b)
        if c.x > d.x: swap(c,d)
        if max(a.x,c.x) > min(b.x,d.x): return false
        if a.y > b.y: swap(a,b)
        if c.y > d.y: swap(c,d)
        return max(a.y,c.y) <= min(b.y,d.y)

    return ab <= 0 and cd <= 0

구현

// Line segment intersection, O(1)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Point { ll x, y; };

ll ccw(Point a, Point b, Point c) {
  ll ret = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
  if (ret > 0) return 1;
  if (ret < 0) return -1;
  return 0;
}

bool intersect(Point a, Point b, Point c, Point d) {
  ll ab = ccw(a,b,c) * ccw(a,b,d);
  ll cd = ccw(c,d,a) * ccw(c,d,b);
  if (ab == 0 && cd == 0) {
      if (a.x > b.x) swap(a,b);
      if (c.x > d.x) swap(c,d);
      if (max(a.x,c.x) > min(b.x,d.x)) return false;
      if (a.y > b.y) swap(a,b);
      if (c.y > d.y) swap(c,d);
      return max(a.y,c.y) <= min(b.y,d.y);
  }
  return ab <= 0 && cd <= 0;
}

int main() {
  Point a{1,1}, b{7,3}, c{2,4}, d{6,1};
  cout << (intersect(a,b,c,d) ? "YES" : "NO") << "\n";
  Point e{0,0}, f{3,3}, g{1,1}, h{2,2};
  cout << (intersect(e,f,g,h) ? "YES" : "NO") << "\n";
}
결과
YES
YES

복잡도

항목
시간O(1) (CCW 4 회 + 비교)
공간O(1)

변형 / 활용

1. 선분 그룹 (Union-Find)

N 개 선분의 교차 관계를 DSU 로 묶으면 연결된 컴포넌트로 그룹화 가능. BOJ 2162.

2. 직선과의 교차

무한 직선과 선분의 교차: ccw(A,B,C) == 0 조건을 제거하고 projection 만 체크.

3. 교차점 좌표 구하기

두 직선의 교점을 parameter form 으로 직접 계산. collinear 가 아닐 때만.

t = cross(C-A, D-C) / cross(B-A, D-C)
P = A + t * (B - A)

4. 선분 교차 개수 (Sweep)

N 개 선분의 모든 교차점을 찾는 Sweeping 알고리즘. O((N+K) log N). Bentley-Ottmann.

함정

1. 정수 오버플로우

CCW 에서 (b.x-a.x) * (c.y-a.y)10^6 * 10^6 = 10^12int (32-bit) 범위 초과. long long 필요.

2. Collinear 겹침 처리

네 점이 일직선 위에 있을 때 ccw 만으로는 겹침 여부를 알 수 없음. Projection range check 필수.

3. 끝점 접촉

한 선분의 끝점이 다른 선분 위에 있는 경우 (< 0 조건) 교차로 인정. ccw() * ccw() < 0 대신 <= 0 사용.

BOJ 연습 문제

번호제목정답률링크
BOJ 17386선분 교차 138.2%kokoa-lab
BOJ 17387선분 교차 227.5%kokoa-lab
BOJ 20149선분 교차 318.3%kokoa-lab
BOJ 2162선분 그룹28.5%kokoa-lab

참고

이 글의 용어 (3개)
볼록 껍질 (Convex Hull)algorithm
정의 볼록 껍질 (Convex Hull) 은 주어진 점 집합 P 를 모두 포함하는 최소 크기의 볼록 다각형. 즉 P 의 어떤 점도 다각형 외부에 있지 않고, 다각형의 꼭짓점은 P…
스위핑 (Sweeping)algorithm
정의 스위핑 (Sweeping) 은 시간 또는 공간 축 위의 이벤트를 정렬한 뒤, 한 방향으로 훑으며 상태를 갱신해 문제를 푸는 기법입니다. 선분 교차, 구간 합집합, 최대 겹침…
Geometry Basic: 벡터, CCW, 외적, 내적algorithm
정의 기하 알고리즘의 밑바탕. 대부분의 2D/3D 기하 문제는 벡터 연산, 내적/외적, CCW 판정 세 가지의 조합으로 풀립니다. 벡터 2D 벡터: 두 점 $A = (ax, ay…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기