추가 설명 — 왜 다음 7개만 비교하면 되는가
가장 가까운 점 쌍 ①에서 “한 점은 다음 7개만 비교하면 된다”고 했다. 왜 하필 몇 개로 끝나는지, 그림으로 천천히 따라가 보자.
- 왜 “몇 개만 보면 된다”가 중요한가
- 가까운 점은 좁은 공간에 빽빽이 못 들어간다는 직관
- 그 직관을 “칸으로 쪼개 세기”로 숫자(7개)까지 굳히기
왜 개수가 중요한가
combine 단계에서 우리는 분할선 좌우로 각각 씩, 합쳐서 폭 인 밴드 안의 점들을 살핀다. 이때 한 점이 밴드 안의 모든 점과 거리를 재면, 밴드에 점이 몰린 경우 한 점당 비교가 번에 달해 전체가 로 돌아간다. 분할 정복으로 얻은 이득이 사라지는 것이다.
그래서 우리에게 필요한 건 한 문장이다: “한 점은 옆의 몇 개만 보면 된다 — 그 개수는 이 아무리 커져도 변하지 않는 상수다.” 이게 보장되면 combine이 가벼워지고 전체가 빨라진다. 이 글의 목표는 그 “몇 개”가 정말 상수임을, 그리고 안전한 값이 7임을 보이는 것이다.
직관: 가까운 점은 빽빽이 못 모인다
핵심 직관부터 잡자. 좌우를 각각 재귀로 푼 뒤, 우리는 이미 알고 있다 — 같은 편 안에서는 어떤 두 점도 보다 가깝지 않다.
왜냐하면 이고, 은 “왼쪽 안 두 점의 최소 거리”이기 때문이다. 즉 왼쪽 점들끼리는 적어도 만큼 떨어져 있다. 오른쪽도 마찬가지다.
점들이 서로 이상 떨어져 있다는 건, 좁은 공간에 점을 많이 욱여넣을 수 없다는 뜻이다. 의자 사이를 최소 만큼 띄워야 한다면 작은 방에 의자를 몇 개밖에 못 놓는 것과 같다. 이 “몇 개밖에 못 놓는다”를 정확한 숫자로 바꾸는 것이 이 글의 전부다.
먼저, 기준점 하나를 잡자. 와 거리 보다 가까운 점은 어디에 있을 수 있을까?
- 같은 편에는 없다. 같은 편 점은 와도 이상 떨어져 있으니까. 를 중심으로 반지름 인 원을 그리면 그 안에 같은 편 점은 하나도 없다.
- 반대편 밴드에서만 올 수 있다. 를 줄여 줄 후보는 오직 맞은편에서 온다.
후보는 좁은 직사각형 안에만 있다
밴드 안 점들을 y좌표 순으로 줄을 세우고, 각 점은 자기보다 위(y가 큰 쪽)에 있는 점만 본다. (아래쪽 점은 그 점이 위를 올려다볼 때 세어지므로, 한 번씩만 보면 된다.)
보다 가까운 점이 있으려면 두 가지를 동시에 만족해야 한다.
- x로 가깝다 — 둘 다 밴드 안이므로 x 방향으로는 최대 (분할선 좌우 각 ) 안에 있다.
- y로 가깝다 — y 차이가 이상이면 그것만으로 거리가 이상이라 볼 필요가 없다. 그러니 y 차이는 미만.
두 조건을 합치면, 위쪽의 후보는 가로 · 세로 인 직사각형 안에만 있을 수 있다.
칸으로 쪼개서 세기
이제 이 직사각형 안에 점이 최대 몇 개 있을 수 있는지 세면 된다. 직접 세긴 어려우니 잘게 쪼개는 트릭을 쓴다.
직사각형을 한 변이 인 작은 정사각형 칸으로 나눈다. 가로 는 짜리 4칸, 세로 는 2칸 — 모두 8칸이다.
분할선이 칸 경계와 딱 맞아떨어지므로, 어떤 칸도 분할선을 걸치지 않는다. 각 칸은 통째로 분할선의 왼쪽이거나 오른쪽이다.
다만 분할선 위에 점이 놓이는 것까지 막을 수는 없다. x좌표가 같은 점이 여럿이면 재귀가 그 점들을 양쪽으로 나눠 갖기 때문이다. 그런 점들은 한 칸에 함께 있으면서 서로 다른 편일 수 있고, 그러면 둘 사이가 이상이라는 보장이 없다. 그래서 「한 칸에는 점이 하나뿐」이라고 세면 안 된다.
대신 편으로 먼저 가른 뒤 센다. 어느 점이 어느 편인지는 재귀가 이미 정해 놓았으므로, 분할선 위에 점이 몇 개 있든 셈이 흐트러지지 않는다.
한 변이 인 정사각형 안에 같은 편 점은 최대 4개까지만 들어갈 수 있다.
증명. 정사각형을 한 변 인 칸 4개로 나눈다. 경계에 걸친 점은 어느 한 칸에만 넣기로 한다.
한 칸에 같은 편 점이 두 개 있다면 둘 사이 거리는 칸의 대각선 보다 짧다. 같은 편 점은 이상 떨어져 있어야 하므로 모순이다. 따라서 칸마다 같은 편 점은 최대 하나이고, 칸이 넷이므로 정사각형 전체로는 최대 4개다. ∎
그래서 “다음 7개”
밴드 안 점들을 y좌표로 정렬하면, 각 점은 y 기준 바로 다음 최대 7개만 비교해도 분할선을 가로지르는 최소 거리 쌍을 놓치지 않는다.
증명. 후보는 가로 ·세로 인 직사각형 안에만 있다. 이 직사각형을 분할선을 경계로 왼쪽 와 오른쪽 로 가른다.
왼쪽 편 점은 x좌표가 분할선 이하이므로 직사각형 안에 있다면 반드시 왼쪽 정사각형 안에 있고, 보조정리 1에 의해 최대 4개다. 오른쪽 편 점도 같은 이유로 최대 4개다. 편으로 갈라 세었으므로 분할선 위에 점이 놓여도 상관없다.
따라서 직사각형 안의 점은 기준점 를 포함해 최대 개이고, 를 빼면 후보는 최대 7개다. y좌표로 줄을 세웠으므로 다음 7개만 보면 이 후보를 빠짐없이 확인하고, 더 뒤의 점은 y 차이가 이미 이상이라 볼 필요가 없다. 이 “7”은 과 무관한 상수다. ∎
이 상수성 덕분에 combine은 점 하나당 일정한 일만 하고, 분할 정복 전체가 빠르게 돌아간다.
맞은편의 한 변 짜리 정사각형 두 칸을 세면 5가 나온다. 한 칸에 최대 3개, 두 칸이면 최대 5개라는 계산이다.
두 숫자는 세는 영역이 다르다. 5는 맞은편 한쪽만 놓고 한 변 짜리 칸 두 개를 센 값이다. 위에서는 분할선 양쪽을 함께 놓고 가로 · 세로 를 센다. 양쪽을 따로 세는 이유는 분할선을 가로지르는 두 점 사이에 이상이라는 제약이 없기 때문이다.
여기서 보인 것은 “7이면 충분하다”는 상한이다. “5로는 부족하다”는 별개의 주장이며, 그것을 말하려면 5개만 비교해서 최소 거리 쌍을 놓치는 배치를 제시해야 한다. 이 글은 거기까지 다루지 않는다.
코드에서는
// 밴드 점들을 y좌표로 정렬한 뒤:
for (int i = le_idx; i <= ri_idx; i++) {
for (int j = 1; j <= 7; j++) { // 다음 7개만
if (i + j > ri_idx) break;
ll td = dist2(arr[i], arr[i + j]);
d = min(d, td);
}
}
안쪽 루프가 7에서 멈추는 것이 위 논거를 그대로 옮긴 것이다. 루프 안에서 y 차이를 다시 확인해 더 일찍 멈추는 구현도 있지만, 어차피 7은 상수라 점근 복잡도는 같다.
- 같은 편 두 점은 거리 이므로, 가까운 점은 좁은 공간에 빽빽이 못 모인다.
- 보다 가까운 후보는 가로 · 세로 직사각형 안에만 있다.
- 이 직사각형을 분할선 기준 왼쪽 와 오른쪽 로 가르면, 한쪽 편은 각 정사각형에 최대 4개.
- 따라서 후보는 포함 최대 개 → 가 비교할 점은 다음 7개면 충분.
- 칸이 아니라 편으로 먼저 가르는 이유는 x좌표가 같은 점들이 분할선 위에서 양쪽으로 갈릴 수 있기 때문이다.
- 이 개수가 과 무관한 상수라는 점이 핵심 — 그래서 combine이 가볍고 분할 정복이 성립한다.
이 설명의 바탕이 되는 전체 알고리즘과 C++ 구현은 가장 가까운 점 쌍 ①에서 다룬다.