최근접 점의 쌍을 찾는 문제는 2차원 평면상의 n개의 점이 입력으로 주어질 때, 거리가 가장 가까운 한 쌍의 점을 찾는 문제.
ClosestPair(S)
입력: x-좌표의 오름차순으로 정렬된 배열 S에는 i개의 점(단, 각 점은 (x,y)로 표현된다.)
출력: S에 있는 점들 중 최근접 점의 쌍의 거리
1. if(i<=3) return (2또는 3개의 점들 사이의 최근접 쌍)
2. 정렬된 S를 같은 크기의 Left_S 와 Right_S로 분할한다.
단, |s|가 홀수이면, |Left_S| = |Right_S|+1이 되도록 분할한다.
3. Left_ClosestPair = ClosestPair(Left_S)
4. Right_ClosestPair = ClosestPair(Right_S)
5. d=min{dist(Left_ClosestPair),dist(Right_ClosestPair)}일 때,
중간 영역에 속하는 점들 중에서 최근접 점의 쌍을 찾아서
이를 Center_ClosestPair라고 하자. dist()는 두 점 사이의 거리
return(min(dist(Left_ClosestPair),dist(Right_ClosestPair),dist(Center_ClosestPair))
왼쪽을 분할 정복한 결과
import random, time, math
# p와 q가 각각 세개의 값으로 이루어짐
def dist(p, q):
return math.sqrt((p[0]-q[0])**2 + (p[1]-q[1])**2)
def closest_pair(a, low, high): # 점들의 배열 a, low,high => 고려해야할 범위
size = high - low + 1
if size == 2:
return [low, high, dist(a[low], a[high])] #내부 점이 2개인 경우, low,high,거리를 반환한다
if size == 3:
# 3개인 경우, 각 low,high를 구하고, 최소거리를 반환한다.
#low, low+1 high ==> 각 점들을 보유한 배열의 인덱스임
s = [[low, low+1, dist(a[low], a[low+1])],
[low, high, dist(a[low], a[high])],
[low+1, high, dist(a[low+1], a[high])]]
[x, y, z] = min(s, key=lambda arr: arr[2])
return [x, y, z]
mid = size // 2 + low - 1
left_pair = closest_pair(a, low, mid)
right_pair = closest_pair(a, mid+1, high)
d = min(left_pair[2], right_pair[2]) # strip의 한쪽 길이
l_boundary = min(z for x, y, z in a if x >= a[mid][0] - d)
r_boundary = max(z for x, y, z in a if x <= a[mid][0] + d)
# strip은 y-좌표로 정렬되도록 b[]로부터 가져온다.
# 원래 이때 y좌표 기준으로 정렬해야했음
strip = [[x, y, z] for x, y, z in b if z >= l_boundary and z <= r_boundary ]
# 중간 영역에서 최근접 점의 쌍을 찾기
c = [[strip[i][2], strip[j][2], dist(strip[i], strip[j])]
for i in range(len(strip)) for j in range(i+1, len(strip)) if i != j]
center_pair = [-1, -1, float('inf')]
if len(c) > 0:
center_pair = min(c, key=lambda arr: arr[2])
t = [left_pair, right_pair, center_pair]
return min(t, key=lambda arr: arr[2]) # 최소 거리 쌍 찾기
#N = 20
#a = []
#random.seed(time.time())
#for i in range(N):
# x = random.randint(0, 200)
# y = random.randint(0, 100)
# a.append([x, y])
a = [[3,3], [8,3], [4,6], [11,7], [6,6], [6,8], [5,1], [1,7], [11,1], [10,9]]
N = len(a)
a.sort(key=lambda t: t[0]) # x-좌표로 정렬
for i in range(N):
a[i].append(i)
print(a)
b = a[:]
b.sort(key=lambda t: t[1]) # y-좌표로 정렬 # 트릭, y축으로 정렬된 배열을 준다. x축에 대한 rank는 그대로 남아있다.
result = closest_pair(a, 0, len(a)-1)
print('분할 정복 해 =\t', a[result[0]][:2], '\t', a[result[1]][:2], '\t %.2f' % result[2])
# O(N**2) 최적해 찾기
c = [[i, j, dist(a[i], a[j])]
for i in range(N) for j in range(i+1,N) if i != j]
c_pair = min(c, key=lambda arr: arr[2])
print('N**2 해 =\t', a[c_pair[0]][:2], '\t', a[c_pair[1]][:2],'\t %.2f' % c_pair[2])
print(c_pair[2] == result[2])