首页 > 编程问答 >如何找到可以包围多边形的最小圆?

如何找到可以包围多边形的最小圆?

时间:2024-07-29 04:29:43浏览次数:10  
标签:python algorithm computational-geometry

使用 python,如何找到可以包围多边形的最小圆?

多边形被定义为映射 2D 平面内顶点的一组坐标。

要求

输入| ||:表示多边形顶点坐标的元组列表。例如:[(x1, y1), (x2, y2), ..., (xn, yn)]. 输出

:包含最小外接圆圆心及其坐标的元组半径。例如:((x_center, y_center), radius)。 约束

: 该算法应该高效并处理合理数量的顶点(例如,最多 1000 个顶点)。 示例

: 对于具有顶点 [(2, 2), (0, 2), (2, 0), (0, 0)] 的输入多边形,输出应该是可以包围所有给定的最小圆的中心和半径顶点(例如((1.0, 1.0), 1.4142))。 可以参考现有的算法、源码库或软件包来寻找最小外接圆,但实现应该是在Python中。

这是已知的作为

最小圆问题 .


import math


def smallest_enclosing_circle(points):
    """
    使用 Welzl 算法查找可以包围给定点集的最小外接圆。

    Args:
        points: 表示多边形顶点坐标的元组列表,例如:[(x1, y1), (x2, y2), ..., (xn, yn)]。

    Returns:
        包含最小外接圆圆心及其半径的元组:((x_center, y_center), radius)。
        如果输入点列表为空,则返回 None。
    """

    def get_distance(p1, p2):
        """计算两点之间的欧几里得距离。"""
        return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)

    def is_point_in_circle(point, circle):
        """检查一个点是否在圆内或圆周上。"""
        center, radius = circle
        return get_distance(point, center) <= radius

    def get_circle_from_2_points(p1, p2):
        """从两个点获得一个圆,其中这些点是直径的端点。"""
        center = ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)
        radius = get_distance(p1, p2) / 2
        return center, radius

    def get_circle_from_3_points(p1, p2, p3):
        """从三个点获得外接圆。"""
        # 如果三个点共线,则返回 None
        if (p2[1] - p1[1]) * (p3[0] - p2[0]) == (p3[1] - p2[1]) * (p2[0] - p1[0]):
            return None
        # 找到外接圆的中心和半径
        a = p2[0] - p1[0]
        b = p2[1] - p1[1]
        c = p3[0] - p1[0]
        d = p3[1] - p1[1]
        e = a * (p1[0] + p2[0]) + b * (p1[1] + p2[1])
        f = c * (p1[0] + p3[0]) + d * (p1[1] + p3[1])
        g = 2 * (a * (p3[1] - p2[1]) - b * (p3[0] - p2[0]))
        center = ((d * e - b * f) / g, (a * f - c * e) / g)
        radius = get_distance(p1, center)
        return center, radius

    def welzl_helper(points, boundary_points):
        """Welzl 算法的递归辅助函数。"""
        if not points and len(boundary_points) <= 3:
            if not boundary_points:
                return None
            elif len(boundary_points) == 1:
                return boundary_points[0], 0
            elif len(boundary_points) == 2:
                return get_circle_from_2_points(*boundary_points)
            else:  # len(boundary_points) == 3
                return get_circle_from_3_points(*boundary_points)

        # 从点集中随机选择一个点
        p = points.pop(0)

        # 递归计算不包括所选点的最小外接圆
        circle = welzl_helper(points.copy(), boundary_points.copy())

        # 如果所选点在圆内,则当前圆是最小外接圆
        if circle and is_point_in_circle(p, circle):
            return circle

        # 否则,所选点必须位于最小外接圆的边界上
        boundary_points.append(p)

        # 递归计算包括所选点作为边界点的最小外接圆
        return welzl_helper(points, boundary_points.copy())

    # 使用空边界点集开始 Welzl 算法
    return welzl_helper(points.copy(), [])


# 示例用法
polygon = [(2, 2), (0, 2), (2, 0), (0, 0)]
circle = smallest_enclosing_circle(polygon)
print(circle)  # 输出:((1.0, 1.0), 1.4142135623730951)

解释:

  1. smallest_enclosing_circle(points) 函数:
  2. 它接收一个点列表(多边形的顶点)作为输入。
  3. 它使用 welzl_helper 函数递归地查找最小外接圆。

  4. welzl_helper(points, boundary_points) 函数:

  5. 它实现了 Welzl 算法,该算法是一种递归算法,它通过递归地缩小可以包含所有点的最小圆的搜索空间来查找可以包含一组点的最小外接圆。
  6. 它有两种基本情况:

    • 如果 points 为空且 boundary_points 中的点数小于或等于 3,则它可以直接计算圆。
    • 如果 points 不为空,则它从 points 中选择一个点 p ,并递归地计算不包括 p 的最小外接圆。然后它检查 p 是否位于圆内。如果位于,则当前圆是最小外接圆。否则, p 必须位于最小外接圆的边界上,并且算法递归地搜索,将 p 包含在 boundary_points 中。
  7. 辅助函数:

  8. get_distance(p1, p2) :计算两点之间的欧几里得距离。
  9. is_point_in_circle(point, circle) :检查一个点是否位于圆内或圆周上。
  10. get_circle_from_2_points(p1, p2) :从两个点创建一个圆,其中这两个点是直径的端点。
  11. get_circle_from_3_points(p1, p2, p3) :从三个点创建一个外接圆(如果三个点共线,则返回 None )。

算法:

Welzl 算法基于以下思想:

  1. 如果圆包含所有点,并且所有边界点(位于圆周上的点)的数量小于或等于 3,则可以确定最小外接圆。
  2. 否则,选择一个不在当前圆上的点,并将该点添加到边界点集中。
  3. 重复该过程,直到找到包含所有点的最小圆。

时间复杂度:

Welzl 算法的预期时间复杂度为 O(n) ,其中 n 是点的数量。这使得它成为解决最小外接圆问题的有效算法,即使对于大量的点也是如此。

标签:python,algorithm,computational-geometry
From: 78793368

相关文章