凸包问题。给定平面上n个点,从中找出一个最小点集,使得该点集所组成的凸多边形包围所有的n个点。基于分治策略,设计一个求解凸包问题的算法。实现该算法并测试。
分治算法思路:
- 如果点集中的点数小于等于3,可以直接返回这些点作为凸包,因为凸包至少包括3个点。
 - 将点集排序,用最左点 P 1 P_1 P1和最右点 P n P_n Pn连成的直线分成两个子集:上包和下包。
 - 获得距离直线最远的点 P m a x P_{max} Pmax
 - 作直线 P 1 P m a x 和 P n P m a x P_1 P_{max}和P_n P_{max} P1Pmax和PnPmax,分别对左侧和右侧求上包
 - 递归地对上包进行凸包计算。
 - 下包同理

 

两点构成的直线函数为:
  
      
       
        
         
          
          
            y 
           
          
            − 
           
           
           
             y 
            
           
             1 
            
           
          
          
           
           
             y 
            
           
             2 
            
           
          
            − 
           
           
           
             y 
            
           
             1 
            
           
          
         
        
          = 
         
         
          
          
            x 
           
          
            − 
           
           
           
             x 
            
           
             1 
            
           
          
          
           
           
             x 
            
           
             2 
            
           
          
            − 
           
           
           
             x 
            
           
             1 
            
           
          
         
        
       
         \frac{y-y_1}{y_2-y_1} = \frac{x-x_1}{x_2-x_1} 
        
       
     y2−y1y−y1=x2−x1x−x1
 代入点到直线的距离公式,得距离为:
  
      
       
        
         
          
          
            ∣ 
           
          
            ( 
           
           
           
             x 
            
           
             2 
            
           
          
            − 
           
           
           
             x 
            
           
             1 
            
           
          
            ) 
           
           
           
             y 
            
           
             3 
            
           
          
            − 
           
          
            ( 
           
           
           
             y 
            
           
             2 
            
           
          
            − 
           
           
           
             y 
            
           
             1 
            
           
          
            ) 
           
           
           
             x 
            
           
             3 
            
           
          
            − 
           
          
            ( 
           
           
           
             x 
            
           
             2 
            
           
          
            − 
           
           
           
             x 
            
           
             1 
            
           
          
            ) 
           
           
           
             y 
            
           
             1 
            
           
          
            + 
           
          
            ( 
           
           
           
             y 
            
           
             2 
            
           
          
            − 
           
           
           
             y 
            
           
             1 
            
           
          
            ) 
           
           
           
             x 
            
           
             1 
            
           
          
            ∣ 
           
          
          
           
           
             ( 
            
            
            
              x 
             
            
              2 
             
            
           
             − 
            
            
            
              x 
             
            
              1 
             
            
            
            
              ) 
             
            
              2 
             
            
           
             + 
            
           
             ( 
            
            
            
              y 
             
            
              2 
             
            
           
             − 
            
            
            
              y 
             
            
              1 
             
            
            
            
              ) 
             
            
              2 
             
            
           
          
         
        
       
         \frac{|(x_2-x_1)y_3-(y_2-y_1)x_3-(x_2-x_1)y_1+(y_2-y_1)x_1|}{\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}} 
        
       
     (x2−x1)2+(y2−y1)2∣(x2−x1)y3−(y2−y1)x3−(x2−x1)y1+(y2−y1)x1∣
 因为我们需要求距离最远,因此只需要计算
  
      
       
        
        
          ∣ 
         
        
          ( 
         
         
         
           x 
          
         
           2 
          
         
        
          − 
         
         
         
           x 
          
         
           1 
          
         
        
          ) 
         
         
         
           y 
          
         
           3 
          
         
        
          − 
         
        
          ( 
         
         
         
           y 
          
         
           2 
          
         
        
          − 
         
         
         
           y 
          
         
           1 
          
         
        
          ) 
         
         
         
           x 
          
         
           3 
          
         
        
          − 
         
        
          ( 
         
         
         
           x 
          
         
           2 
          
         
        
          − 
         
         
         
           x 
          
         
           1 
          
         
        
          ) 
         
         
         
           y 
          
         
           1 
          
         
        
          + 
         
        
          ( 
         
         
         
           y 
          
         
           2 
          
         
        
          − 
         
         
         
           y 
          
         
           1 
          
         
        
          ) 
         
         
         
           x 
          
         
           1 
          
         
        
          ∣ 
         
         
        
          = 
         
        
          ∣ 
         
        
          x 
         
        
          1 
         
        
          ∗ 
         
        
          y 
         
        
          2 
         
        
          + 
         
        
          x 
         
        
          3 
         
        
          ∗ 
         
        
          y 
         
        
          1 
         
        
          + 
         
        
          x 
         
        
          2 
         
        
          ∗ 
         
        
          y 
         
        
          3 
         
        
          − 
         
        
          x 
         
        
          3 
         
        
          ∗ 
         
        
          y 
         
        
          2 
         
        
          − 
         
        
          x 
         
        
          2 
         
        
          ∗ 
         
        
          y 
         
        
          1 
         
        
          − 
         
        
          x 
         
        
          1 
         
        
          ∗ 
         
        
          y 
         
        
          3 
         
        
          ∣ 
         
        
       
         |(x_2-x_1)y_3-(y_2-y_1)x_3-(x_2-x_1)y_1+(y_2-y_1)x_1| \\ =|x1 * y2 + x3 * y1 + x2 * y3 - x3 * y2 - x2 * y1 - x1 * y3| 
        
       
     ∣(x2−x1)y3−(y2−y1)x3−(x2−x1)y1+(y2−y1)x1∣=∣x1∗y2+x3∗y1+x2∗y3−x3∗y2−x2∗y1−x1∗y3∣
 最大即可。
代码如下:
import random
import matplotlib.pyplot as plt
from typing import List
# slot是为了减少内存开销, lt是为了排序, iter是为了方便取值
class Point:
    __slots__ = ['x', 'y']
    def __init__(self, x:float, y:float) -> None:
        self.x = x
        self.y = y
    def __lt__(self, other):
        if self.x == other.x:
            return self.y < other.y
        return self.x < other.x
    def __iter__(self):
        yield self.x
        yield self.y
def cacl_dis(a:Point, b:Point, c:Point) -> float:
    x1, y1 = a
    x2, y2 = b
    x3, y3 = c
    return x1 * y2 + x3 * y1 + x2 * y3 - x3 * y2 - x2 * y1 - x1 * y3
def border_point_up(left_point:Point, right_point:Point, lists:List[Point], border_points:List[Point]) -> None:
    dis_max = 0
    max_point = None
    for item in lists:
        if item == left_point or item == right_point:
            continue
        else:
            dis = cacl_dis(left_point, right_point, item)
            if dis > dis_max:
                max_point = item
                dis_max = dis
    if dis_max != 0:
        border_points.append(max_point)
        border_point_up(left_point, max_point, lists, border_points)
        border_point_up(max_point, right_point, lists, border_points)
def border_point_down(left_point:Point, right_point:Point, lists:List[Point], border_points:List[Point]) -> None:
    dis_max = 0
    max_point = ()
    for item in lists:
        if item == left_point or item == right_point:
            continue
        else:
            dis = cacl_dis(left_point, right_point, item)
            if dis < dis_max:
                max_point = item
                dis_max = dis
    if dis_max != 0:
        border_points.append(max_point)
        border_point_down(left_point, max_point, lists, border_points)
        border_point_down(max_point, right_point, lists, border_points)
def order_border(lists: List[Point]) -> List[Point]:
    lists.sort()
    first_x, first_y = lists[0]  # 最左边的点
    last_x, last_y = lists[-1]  # 最右边的点
    list_border_up = []  # 上半边界
    for item in lists:
        x, y = item
        if y > max(first_y, last_y):
            list_border_up.append(item)
        if min(first_y, last_y) < y < max(first_y, last_y):
            if cacl_dis(lists[0], lists[-1], item) > 0:
                list_border_up.append(item)
            else:
                continue
    list_border_down = [_ for _ in lists if _ not in list_border_up]  # 下半边界
    list_end = list_border_up + list_border_down[::-1]  # 最终顺时针输出的边界点
    return list_end
def draw(list_points:List[Point], list_borders:List[Point]) -> None:
    list_all_x = []
    list_all_y = []
    for item in list_points:
        a, b = item
        list_all_x.append(a)
        list_all_y.append(b)
    list_borders.append(list_borders[0])
    plt.scatter(list_all_x, list_all_y)
    for i in range(len(list_borders) - 1):
        one_, oneI = list_borders[i]
        two_, twoI = list_borders[i + 1]
        plt.plot([one_, two_], [oneI, twoI], color='red')
        plt.scatter([one_, two_], [oneI, twoI], color='red')
    plt.show()
# 生成随机点集
def generate_random_points(n:int, xmin:float, xmax:float, ymin:float, ymax:float)->List[Point]:
    return [Point(random.uniform(xmin, xmax), random.uniform(ymin, ymax)) for _ in range(n)]
if __name__ == "__main__":
    n = 100
    x_min, x_max = 0, 100
    y_min, y_max = 0, 100
    list_points = generate_random_points(n, x_min, x_max, y_min, y_max)
    list_points.sort()
    border_points = []  # 边界点集
    border_point_up(list_points[0], list_points[-1], list_points, border_points)  # 上边界点集
    border_point_down(list_points[0], list_points[-1], list_points, border_points)  # 下边界点集
    border_points.append(list_points[0])
    border_points.append(list_points[-1])
    draw(list_points, order_border(border_points))
 
这里随机初始化100个点来测试代码,并且将结果通过matplotlib展示出来:
 



















