📜  使用分而治之算法的凸包(1)

📅  最后修改于: 2023-12-03 15:22:22.539000             🧑  作者: Mango

使用分而治之算法的凸包

凸包是由一系列点所组成的最小凸多边形。而分而治之算法是一种将问题分解为更小更容易解决的子问题,再将每个子问题的解合并为总问题的解的算法。在计算凸包时使用分而治之算法可以大大提高算法的效率。

算法实现

通过分而治之算法求解凸包需要两个步骤:分解和合并。

分解

分解步骤将点集合分成两个或多个部分,对每一部分递归地求解凸包。具体实现时可以按照横坐标的大小将点集合分成两个部分,再在两个部分中进行递归求解。

def divide(points):
    n = len(points)
    if n <= 5:
        return brute(points)
    mid = n // 2
    left = divide(points[:mid])
    right = divide(points[mid:])
    return merge(left, right)
合并

合并步骤将求解得到的两个凸包合并为总凸包。具体实现时需要考虑两个凸包相交的情况,将相交的部分去掉后再将两个凸包拼接起来。

def merge(left, right):
    l = len(left) - 1
    r = len(right) - 1
    while cross(left[l] - left[l - 1], right[1] - right[0]) > 0 and cross(right[r] - right[r - 1], left[1] - left[0]) > 0:
        if left[l].y < right[r].y:
            r -= 1
        else:
            l -= 1
    lower = left[:l + 1] + right[:r + 1]
    l = len(left) - 1
    r = len(right) - 1
    while cross(left[l] - left[l - 1], right[1] - right[0]) < 0 and cross(right[r] - right[r - 1], left[1] - left[0]) < 0:
        if left[l].y > right[r].y:
            r -= 1
        else:
            l -= 1
    upper = left[l:] + right[r:]
    return lower + upper[1:-1]
暴力求解小规模问题

当分解得到的点集合大小不超过5时采用暴力求解的方式。

def brute(points):
    hull = []
    for i in range(len(points)):
        hull.append(points[i])
        while len(hull) > 2 and cross(hull[-1] - hull[-2], hull[-3] - hull[-2]) > 0:
            hull.pop(-2)
    return hull
算法性能

使用分而治之算法求解凸包的时间复杂度为O(n log n)。在实际应用中可以通过将点集合按照x坐标的大小排序来进一步提高算法效率。

总结

分而治之算法可以大大提高凸包的求解效率,适用于大规模的点集合。程序员可以通过学习这个算法来提高自己的程序设计能力,扩展自己的算法解决方案。