凸包代码 python

本文详细介绍了Graham-Scan算法用于计算二维平面上点集的凸包,并实现了该算法。通过随机生成不同数量的点,测试了算法的运行时间,最后用matplotlib展示了算法在不同数据规模下的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import matplotlib.pyplot as plt
import math
import time
import numpy as np

start_t = time.clock()

# points中纵坐标最小点的索引,若有多个则返回横坐标最小的点
def get_bottom_point(points):
    min_index = 0
    n = len(points)
    for i in range(0, n):
        if points[i][1] < points[min_index][1] or (
                points[i][1] == points[min_index][1] and points[i][0] < points[min_index][0]):
            min_index = i
    return min_index


# 按照与中心点的极角进行排序,余弦,center_point: 中心点
def sort_polar_angle_cos(points, center_point):
    n = len(points)
    cos_value = []
    rank = []
    norm_list = []
    for i in range(0, n):
        point_ = points[i]
        point = [point_[0] - center_point[0], point_[1] - center_point[1]]
        rank.append(i)
        norm_value = math.sqrt(point[0] * point[0] + point[1] * point[1])
        norm_list.append(norm_value)
        if norm_value == 0:
            cos_value.append(1)
        else:
            cos_value.append(point[0] / norm_value)

    for i in range(0, n - 1):
        index = i + 1
        while index > 0:
            if cos_value[index] > cos_value[index - 1] or (
                    cos_value[index] == cos_value[index - 1]
                    and norm_list[index] > norm_list[index - 1]):
                temp = cos_value[index]
                temp_rank = rank[index]
                temp_norm = norm_list[index]
                cos_value[index] = cos_value[index - 1]
                rank[index] = rank[index - 1]
                norm_list[index] = norm_list[index - 1]
                cos_value[index - 1] = temp
                rank[index - 1] = temp_rank
                norm_list[index - 1] = temp_norm
                index = index - 1
            else:
                break
    sorted_points = []
    for i in rank:
        sorted_points.append(points[i])

    return sorted_points


# 返回向量与向量[1, 0]之间的夹角-从[1, 0]沿逆时针方向旋转多少度能到达这个向量
def vector_angle(vector):
    norm_ = math.sqrt(vector[0] * vector[0] + vector[1] * vector[1])
    if norm_ == 0:
        return 0

    angle = math.acos(vector[0] / norm_)
    if vector[1] >= 0:
        return angle
    else:
        return 2 * math.pi - angle


# 两向量的叉乘
def coss_multi(v1, v2):
    return v1[0] * v2[1] - v1[1] * v2[0]


def graham_scan(points):
    bottom_index = get_bottom_point(points)
    bottom_point = points.pop(bottom_index)
    sorted_points = sort_polar_angle_cos(points, bottom_point)

    m = len(sorted_points)
    if m < 2:
        print("点的数量过少,无法构成凸包")
        return

    stack = []
    stack.append(bottom_point)
    stack.append(sorted_points[0])
    stack.append(sorted_points[1])
    # print('当前stack', stack)

    for i in range(2, m):
        length = len(stack)
        top = stack[length - 1]
        next_top = stack[length - 2]
        v1 = [sorted_points[i][0] - next_top[0], sorted_points[i][1] - next_top[1]]
        v2 = [top[0] - next_top[0], top[1] - next_top[1]]

        while coss_multi(v1, v2) >= 0:
            if length < 3:     # 加上这两行代码之后,数据量很大时不会再报错
                break          # 加上这两行代码之后,数据量很大时不会再报错
            stack.pop()
            length = len(stack)
            top = stack[length - 1]
            next_top = stack[length - 2]
            v1 = [sorted_points[i][0] - next_top[0], sorted_points[i][1] - next_top[1]]
            v2 = [top[0] - next_top[0], top[1] - next_top[1]]
        stack.append(sorted_points[i])

    return stack


# 产生随机点
n_iter = [100, 500, 1000, 2000, 3000]
time_cost = []
for n in n_iter:
    points = []
    for i in range(n):
        point_x = np.random.randint(1, 100)
        point_y = np.random.randint(1, 100)
        temp = np.hstack((point_x, point_y))
        point = temp.tolist()
        points.append(point)

    result = graham_scan(points)

    # 记录程序运行时间
    end_t = time.clock()
    time_iter = end_t - start_t
    print("Graham-Scan算法运行时间:", time_iter)
    # draw(list_points, border_line)
    time_cost.append(time_iter)
    # 画结果图
    """
    for point in points:
        plt.scatter(point[0], point[1], marker='o', c='y', s=8)
    length = len(result)
    for i in range(0, length - 1):
        plt.plot([result[i][0], result[i + 1][0]], [result[i][1], result[i + 1][1]], c='r')
    plt.plot([result[0][0], result[length - 1][0]], [result[0][1], result[length - 1][1]], c='r')
    plt.show()
    """

# 不同测试集下的运行时间
plt.plot(n_iter, time_cost)
plt.show()

 

### 凸包算法Python实现 #### 使用Scipy库计算凸包 为了从给定的数据点集合中获取凸包,可以利用`scipy.spatial.ConvexHull`模块。下面展示了如何针对每个簇中的种子点来构建并存储它们各自的凸包对象。 ```python from scipy.spatial import ConvexHull # 假设X_seeds是一个字典,其中键是索引而值是由坐标组成的数组列表;indices也是一个字典形式表示各簇内的成员资格。 hulls = {} for i in indices: hull = ConvexHull(X_seeds[indices[i]]) hulls[i] = hull # 将每组数据对应的ConvexHull实例存入字典[^1] ``` 这段代码遍历了所有的簇,并为每一个创建了一个`ConvexHull`对象,该对象包含了构成相应簇边界所需的信息。 #### 自定义Ouellet凸包算法 对于更深入的学习者来说,可能希望探索不同的方法论或优化现有解决方案。Ouellet提出的凸包算法提供了一种替代方案,其特点在于效率较高且易于理解。虽然官方提供的资源链接指向的是C++版本的源码文件[^2],但是可以根据这些原理自行开发相应的Python版程序。 以下是基于上述理论的一个简化示例: ```python def ouellet_convex_hull(points): """A simplified version of the Ouellet convex hull algorithm.""" def cross(o, a, b): return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) lower = [] upper = [] sorted_points = sorted(set(points)) # Remove duplicates and sort by x-coordinate. for p in sorted_points: while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0: lower.pop() lower.append(p) for p in reversed(sorted_points): while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0: upper.pop() upper.append(p) return lower[:-1] + upper[:-1] points = [(random.uniform(-10, 10), random.uniform(-10, 10)) for _ in range(30)] result = ouellet_convex_hull(points) print(result) ``` 此函数实现了Ouellet算法的核心逻辑——通过维护上下两条链表分别记录当前已知的最佳候选边界的端点位置,并最终返回完整的多边形顶点序列作为结果集的一部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alocus_

如果我的内容帮助到你,打赏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值