Exemplo n.º 1
0
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])