Finding largest subset of points forming a convex polygon

前端 未结 4 841
名媛妹妹
名媛妹妹 2021-02-05 12:24

I\'m looking for an algorithm for finding largest subset of points (by largest i mean in number) that form a convex polygon from the given set of point. I think this might be so

4条回答
  •  一个人的身影
    2021-02-05 13:19

    I think I came up with an algorithm for it by extending Andrew's algorithm for convex hulls.

    Originally, I came up with a O(N^4) algorithm, but noticed was way over-complicating it and brought it down to O(N^2) algorithm. It seems like there maybe a bug in the code somewhere that causes issues in at least the case of a vertical line of points. It might be a special case (requiring changing a few lines of code), or a sign of a larger flaw in the algorithm. If it's the latter, then I'm inclined to say it's NP-hard, and offer the algorithm as a heuristic. I've spent all the time I care to coding and debugging it. In any case it seems to work fine in other cases. However, it's difficult to test for correctness when the correct algorithms seem to be O(2^N).

    def maximal_convex_hull(points):
        # Expects a list of 2D points in the format (x,y)
    
    
        points = sorted(set(points)) # Remove duplicates and sort
        if len(points) <= 1: # End early
            return points
    
        def cross(o, a, b): # Cross product
            return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
    
    
        # Use a queue to extend Andrew's algorithm in the following ways:
        # 1. Start a new convex hull for each successive point
        # 2. Keep track of the largest convex hull along the way
        Q = []
        size = 0
        maximal_hull = None
        for i,p in enumerate(points):
            remaining = len(points) - i + 1
            new_Q = []
            for lower, upper in Q: # Go through the queue
                # Build upper and lower hull similtanously, slightly less efficent
                while len(lower) >= 2 and cross(lower[-2], lower[-1], p) < 0:
                    lower.pop()
                lower.append(p)
                while len(upper) >= 2 and cross(upper[-2], upper[-1], p) > 0:
                    upper.pop()
                upper.append(p)
    
                new_size = len(lower) + len(upper)
                if new_size > size: # Check for a new highest maximal convex hull
                    size = new_size
                    maximal_hull = (lower, upper)
    
    
            # There is an odd bug? that when one or both if statements are removed
            #  xqg237.tsp (TSPLIB) gives slightly different solutions and is
            #   missing a number of points it should have.
            #  Seems like if the opposite should be true if anything since they
            #   were intended to be easy optimizations not required code.
                if remaining + new_size >= size: # Don't bother if not enough
                    new_Q.append((lower, upper)) # Add an updated convex hulls
            if remaining > size: # Don't bother if not enough
                new_Q.append(([p], [p])) # Add a new convex hull
            Q = new_Q # Update to the new queue
    
        # Extract and merge the lower and upper to a maximium convex hull
        # Only one of the last points is ommited because it is repeated
        #    Asserts and related variables used for testing
        #    Could just have "return lower[:-1] + list(reversed(upper[:-1]))[:-1]"
        lower, upper = maximal_hull
        max_hull_set = set(lower) | set(lower) | set(upper)
        lower = lower
        upper = list(reversed(upper[:-1]))[:-1]
        max_convex_hull = lower + upper
        assert len(lower) + len(upper) == len(max_hull_set)
        assert max_hull_set == set(max_convex_hull)
        return max_convex_hull
    

    Edit: This algorithm isn't correct, but it served as inspiration for the correct algorithm and thus I'm leaving it here.

提交回复
热议问题