def gift_wrapping_2d(points): """Computes the convex hull of a list of tuples (points) in O(n^2). This is also called the Jarvis March and it is the simplest of the convex hull algorithms. @param points - List of tuples for the points in the set. @return hull - List of tuples for the points on the convex hull. """ if not points: return [] def _turn_direction(a, b, c): """Returns the turn direction of the joint a, b, c. This returns 1 if it's a right turn, 0 if it's a straight line, or -1 if it's a left turn. """ turn_val = (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1]) return -1 * cmp(turn_val, 0) hull = [min(points)] for pt in hull: # Find the next point in the hull (find the point that corresponds to the # largest turn angle (see http://tinyurl.com/qc4dnae). cur = pt for next_pt in points: turn = _turn_direction(pt, cur, next_pt) if turn == -1 or (turn == 0 and dist_squared(pt, next_pt) > dist_squared(pt, cur)): cur = next_pt if cur == hull[0]: break; hull.append(cur) return hull
def closest_pair_in_subset(P): """ Divide and conquer, find the left-side and right-side min distance Find min distance among set of pairs of points which one point lies on the left and the other on the right. The minimum is between d_l, d_r, d_lr """ if len(P) == 2: return P mid = len(P) / 2 # Slice into even subsets left = P[:mid + len(P) % 2] right = P[mid:] # Divide and conquer p_l = closest_pair_in_subset(left) p_r = closest_pair_in_subset(right) # Compare the squared dist of both sides (euclidean dist is expensive because of the square root # p_min is the set of points with min distance d_min, p_min = (dist_pair(p_l), p_l) if dist_pair(p_l) < dist_pair(p_r) else (dist_pair(p_r), p_r) # Find candidate points with x < d_min from x_mid x_mid = P[mid] cand_left = [p for p in left if x_mid[0] - p[0] < d_min] # If odd, slice the last point in cand_left, it will be included in the right if len(P) % 2 == 1: cand_left = cand_left[:-1] # Compare min distances with those to the right for l in cand_left: for r in right: if r[0] - l[0] >= d_min: break if abs(r[1] - l[1]) >= d_min: continue d = dist_squared(l, r) if d < d_min: d_min = d p_min = [l, r] return p_min
def dist_pair(p): if len(p) is not 2: raise ValueError('Needs exactly 2 points') return dist_squared(p[0], p[1])