I am trying to implement the closest pair problem in Python using divide and conquer, everything seems to work fine except that in some input cases, there is a wrong answer. My
You have two problems, you are forgetting to call dist to update the best distance. But the main problem is there is more than one recursive call happening so you can end up overwriting when you find a closer split pair with the default, best,p3,q3 = d,None,None
. I passed the best pair from closest_pair
as an argument to closest_split_pair
so I would not potentially overwrite the value.
def closest_split_pair(p_x, p_y, delta, best_pair): # <- a parameter
ln_x = len(p_x)
mx_x = p_x[ln_x // 2][0]
s_y = [x for x in p_y if mx_x - delta <= x[0] <= mx_x + delta]
best = delta
for i in range(len(s_y) - 1):
for j in range(1, min(i + 7, (len(s_y) - i))):
p, q = s_y[i], s_y[i + j]
dst = dist(p, q)
if dst < best:
best_pair = p, q
best = dst
return best_pair
The end of closest_pair looks like the following:
p_1, q_1 = closest_pair(srt_q_x, srt_q_y)
p_2, q_2 = closest_pair(srt_r_x, srt_r_y)
closest = min(dist(p_1, q_1), dist(p_2, q_2))
# get min of both and then pass that as an arg to closest_split_pair
mn = min((p_1, q_1), (p_2, q_2), key=lambda x: dist(x[0], x[1]))
p_3, q_3 = closest_split_pair(p_x, p_y, closest,mn)
# either return mn or we have a closer split pair
return min(mn, (p_3, q_3), key=lambda x: dist(x[0], x[1]))
You also have some other logic issues, your slicing logic is not correct, I made some changes to your code where brute is just a simple bruteforce double loop:
def closestPair(Px, Py):
if len(Px) <= 3:
return brute(Px)
mid = len(Px) / 2
# get left and right half of Px
q, r = Px[:mid], Px[mid:]
# sorted versions of q and r by their x and y coordinates
Qx, Qy = [x for x in q if Py and x[0] <= Px[-1][0]], [x for x in q if x[1] <= Py[-1][1]]
Rx, Ry = [x for x in r if Py and x[0] <= Px[-1][0]], [x for x in r if x[1] <= Py[-1][1]]
(p1, q1) = closestPair(Qx, Qy)
(p2, q2) = closestPair(Rx, Ry)
d = min(dist(p1, p2), dist(p2, q2))
mn = min((p1, q1), (p2, q2), key=lambda x: dist(x[0], x[1]))
(p3, q3) = closest_split_pair(Px, Py, d, mn)
return min(mn, (p3, q3), key=lambda x: dist(x[0], x[1]))
I just did the algorithm today so there are no doubt some improvements to be made but this will get you the correct answer.