【算法】Graham 凸包扫描算法 ( 凸包概念 | 常用的凸包算法 | 角排序 | 叉积 | Python 代码示例 )

news2024/10/7 10:25:57

文章目录

  • 一、Graham 凸包扫描算法
    • 1、凸包概念
    • 2、常用的凸包算法
    • 3、Graham 凸包扫描算法
  • 二、Graham 算法前置知识点
    • 1、角排序
    • 2、叉积
    • 3、算法过程分析
  • 三、代码示例
    • 1、完整代码示例
    • 2、执行结果


使用 Graham 算法绘制的凸包效果 :
在这里插入图片描述

博客代码下载 : https://download.csdn.net/download/han1202012/89428182

  • 使用 PyCharm 打开 , 使用 Python 3.9 开发 ;
    在这里插入图片描述




一、Graham 凸包扫描算法




1、凸包概念


凸包概念 : 在二维平面中 , 包围点集的最小凸多边形 , 其顶点集包含了给定点集中的所有点 , 并且不存在任何一条线段可以穿过这个多边形的内部而不与多边形的边界相交 ;

下图中 ,

  • 左侧的 P1 图是凸包 ;
  • 右侧的 P2 图不是凸包 , 因为该图中 , A2 到 B2 的点连接线与 凸多边形 的边界发生了相交 ;
    在这里插入图片描述

2、常用的凸包算法


常用的凸包算法有 :

  • Graham 扫描法
  • Jarvis 步进法
  • 快速凸包算法

3、Graham 凸包扫描算法


在二维平面上给出一个有限个点的点集 , 其坐标都为 (x , y) ;

Graham 格雷厄姆 凸包扫描算法 , 可以找到上述点集的 凸包边界 , 其时间复杂度是 O(nlogn) ;





二、Graham 算法前置知识点




1、角排序


角排序 是 以角度大小进行排序 , 这里的角度是 选定的基准点 与 点集中的点 的 极角 进行排序 ;

角排序 是一种在计算几何学和算法设计中常用的技术 , 用于对点集中的点按照其与某一基准点的极角进行排序 ;

极角 , 又称为 " 极坐标角度 " , 是指一个点相对于 极点 与 极轴 之间的夹角 , 极角通常用来描述点在 极坐标系 中的位置 ;

  • 极点 是 中心的点 ;
  • 极轴 是 水平 x 轴 ;

极坐标系如下图所示 , 一个点的位置由 极角 ( 从极轴到点到极点连线的方向的角度 ) 和 极径 ( 点到极点的距离 ) 确定 ;

在这里插入图片描述


在角排序中 , 极角是指从基准点出发到其他点的连线与某一固定方向的夹角 ;

角排序用于解决凸包算法中的子问题 , 例如 Graham 扫描算法中 , 需要对点集中的点按照其与基准点的极角进行排序 , 以便确定凸包的边界顺序 ;


在本算法中 , 以极坐标的原点为中心 , 进行角排序 ;


2、叉积


叉积 , 又称为 " 向量积 " 或 " 矢量积 " , 是两个向量之间的一种运算 , 叉积 的结果是一个新的向量 , 值为两个向量所张成的平行四边形的面积 , 方向垂直于这个平行四边形的平面 , 符合右手定则 ;

判断 B 点 在 向量 OA 的左侧还是右侧 :

  • B 在 向量 OA 左侧 , 则 OA 与 OB 的 叉积为负数 ;
  • B 在 向量 OA 右侧 , 则 OA 与 OB 的 叉积为正数 ;
    在这里插入图片描述

给定平面上 3 个点 ABC , 叉积 可以判断一个 点 C 在向量 AB 的哪一边 ,

  • 如果 C 点在 向量 AB 左边 , 则 AB 与 AC 的叉积为正 ;
  • 如果 C 点在 向量 AB 右边 , 则 AB 与 AC 的叉积为负 ;

3、算法过程分析


设置一个 栈 数据结构 ,

将左下角的 2 个点放入 栈 中 ,

从 第三个点开始循环 , 循环内容如下 :

先将要遍历的点放入 栈中 , 判断 新放入的点 是否在 栈顶 2 个元素组成的向量的左边 ,

  • 如果在左边 , 说明该点是凸包上的点 , 栈中保留该点 , 则继续遍历下一个点 ;
  • 如果在右边 , 说明该点不是凸包上的点 , 从栈中弹出该点 , 继续遍历下一个点 ;




三、代码示例



博客代码下载 : https://download.csdn.net/download/han1202012/89428182

  • 使用 PyCharm 打开 , 使用 Python 3.9 开发 ;

1、完整代码示例


import tkinter as tk    # 导入 Tkinter 模块
import random           # 导入 random 模块用于生成随机数
import math             # 导入 math 模块用于数学计算

# 定义点类
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 计算两点之间的距离
def distance(p1, p2):
    return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)

# 通过计算叉乘计算 3 点方向
#   如果叉乘结果 = 0 , 则说明 p1/p2/p3 共线
#   如果叉乘结果 > 0 , 则为顺时针方向
#   如果叉乘结果 < 0 , 则为逆时针方向
def cross_product(p1, p2, p3):
    return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x)

# 求点集的极角
def polar_angle(p0, p):
    return math.atan2(p.y - p0.y, p.x - p0.x)

# 计算两点之间的距离的平方
def distance_squared(p1, p2):
    return (p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2

# 角排序函数
def angle_sort(points):
    # 点集个数必须大于 3 个
    if len(points) < 2:
        return points

    # 找到最左下方的点作为起始点
    # p0 点作为极坐标的原点
    p0 = min(points, key=lambda p: (p.y, p.x))
    # 按照极角和距离的平方排序
    # 先按照极角进行排序 如果极角相同 则按照 p0 点到该点的距离排序
    sorted_points = sorted(points, key=lambda p: (polar_angle(p0, p), distance_squared(p0, p)))
    # 返回按照极角进行排序的 Point 集合
    return [p0] + sorted_points[1:]

# Graham 扫描法找凸包
def graham_scan(points):
    if len(points) < 3:
        return points

    # 进行角排序
    sorted_points = angle_sort(points)
    # 栈数据结构
    stack = []
    for p in sorted_points:
        # 如果栈中的元素大于 2 个开始执行循环, 计算 p 点 在 栈顶两个元素(倒数第二个在前,倒数第一个再后)组成的向量的左侧还是右侧
        while len(stack) >= 2 and cross_product(stack[-2], stack[-1], p) <= 0:
            # 如果 p 点在栈顶两个元素组成的向量的左侧 则说明该点是凸边中的点 , 栈顶元素不是凸边中的点 , 将栈顶出栈 , 将本元素入栈
            stack.pop()
        # 向栈中添加新元素
        stack.append(p)
    return stack

# 生成随机点集
# 界面为 800x600 , x 方向上在 50 ~ 750 之间生成点, y 方向上在 50 ~ 550 之间生成点
def generate_points(num_points):
    points = []
    for _ in range(num_points):
        x = random.randint(50, 750)  # 生成 x 坐标范围在 50 到 750 之间的随机数
        y = random.randint(50, 550)  # 生成 y 坐标范围在 50 到 550 之间的随机数
        points.append(Point(x, y))
    return points

# 在画布上绘制点
def draw_points(canvas, points):
    for point in points:
        canvas.create_oval(point.x - 2, point.y - 2, point.x + 2, point.y + 2, fill="blue")  # 绘制圆点

# 在画布上绘制凸包
def draw_convex_hull(canvas, convex_hull):
    for i in range(len(convex_hull)):
        p1 = convex_hull[i]
        p2 = convex_hull[(i + 1) % len(convex_hull)]
        canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill="red")  # 绘制凸包的边

# 主函数
def main():
    num_points = 100                       # 点的数量设置为 200 个
    points = generate_points(num_points)    # 生成随机点集
    convex_hull = graham_scan(points)  # 使用 Graham 扫描法找凸包

    root = tk.Tk()  # 创建 Tkinter 窗口
    root.title("Graham Scan Convex Hull")  # 设置窗口标题
    canvas = tk.Canvas(root, width=800, height=600, bg="white")  # 创建画布
    canvas.pack()  # 将画布放置在窗口中央

    draw_points(canvas, points)  # 绘制点集
    draw_convex_hull(canvas, convex_hull)  # 绘制凸包

    root.mainloop()  # 进入 Tkinter 主循环

if __name__ == "__main__":
    main()  # 调用主函数


2、执行结果


执行上述代码后 , 在画面中随机生成了 100 个点 , 并进行 Graham 扫描算法 , 计算出了点集的凸集 , 绘制效果如下 :

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1818956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提升设计效率,选择亿达四方SolidWorks代理服务

在当今快速发展的设计和制造行业中&#xff0c;拥有高效、可靠的工具是企业保持竞争力的关键。作为设计领域的领军软件&#xff0c;SolidWorks以其强大的三维建模功能、直观的用户界面以及广泛的行业应用而闻名。然而&#xff0c;要充分发挥这款软件的潜力&#xff0c;选择一个…

2024下《系统分析师》50个高频考点汇总!背就有效

宝子们&#xff01;上半年软考已经结束一段时间了&#xff0c;准备备考下半年软考高级-系统分析师的小伙伴可以开始准备了&#xff0c;毕竟高级科目的难度可是不低的&#xff0c;相信参加过上半年系分的小伙伴深有体会。 这里给大家整理了50个高频考点&#xff0c;涵盖全书90%…

DDei在线设计器-配置主题风格

DDeiCore-主题 DDei-Core插件提供了默认主题和黑色主题。 如需了解详细的API教程以及参数说明&#xff0c;请参考DDei文档 默认主题 黑色主题 使用指南 引入 import { DDeiCoreThemeBlack } from "ddei-editor";使用并修改设置 extensions: [......//通过配置&am…

Java老人护理上门服务类型系统小程序APP源码

&#x1f338; 老人上门护理服务系统&#xff1a;温暖与专业并存 &#x1f338; 一、&#x1f3e0; 走进老人上门护理服务系统 随着社会的快速发展&#xff0c;我们越来越关注老年人的生活质量。老人上门护理服务系统应运而生&#xff0c;它结合了现代科技与人性化服务&#…

stable-diffusion.cpp 文字生成图片

纯 C/C 中 [Stable Diffusion] 的推断 https://github.com/CompVis/stable-diffusion ## 特点 - 基于 [ggml]&#xff08;https://github.com/ggerganov/ggml&#xff09; 的普通 C/C 实现&#xff0c;工作方式与 [llama.cpp]&#xff08;https://github.com/ggerganov/llam…

Python提取PowerPoint演示文稿表格保存到文本及Excel文件

PowerPoint作为广泛使用的演示工具&#xff0c;常被用于展示各类数据报告和分析结果&#xff0c;其中&#xff0c;表格以其直观性和结构性成为阐述数据关系的不二之选。然而&#xff0c;在数据分析、文档归档或跨平台分享的场景下&#xff0c;幻灯片中的表格功能难以满足需求&a…

电脑桌面提醒做事的app 好用的桌面提醒app

在快节奏的现代生活中&#xff0c;我们每天都要通过电脑处理大量的工作事项。然而&#xff0c;繁忙的工作节奏有时会导致我们遗忘某些重要任务&#xff0c;从而带来不必要的损失。为了避免这种情况&#xff0c;选择一款好用的桌面提醒app显得尤为重要。 想象一下&#xff0c;你…

Java中的方法重写与重载

在Java编程语言中&#xff0c;方法重写&#xff08;Override&#xff09;和方法重载&#xff08;Overload&#xff09;是实现代码多态性的两种基本方式。它们允许程序员以多种方式使用相同的方法名&#xff0c;增加了程序的可读性和可重用性&#xff0c;但它们的应用场景和规则…

一文读懂Partisia Blockchain 的MOCCA方案:资产托管的最优解

Partisia Blockchain是一个兼具隐私、可互操以及高迸发特性的Layer1系统&#xff0c;其通过将区块链以及零知识计算&#xff08;包括MPC、零知识证明ZKP等&#xff09;以协作的方式结合起来&#xff0c;并通过分片方案、Bring Your Own Coin&#xff08;BYOC&#xff09;功能和…

文心一言 VS 讯飞星火 VS chatgpt (280)-- 算法导论20.4 1题

一、假设 CONNECTED-COMPONENTS 作用于一个无向图 G(V&#xff0c;E)&#xff0c;这里V{a&#xff0c;b&#xff0c;c&#xff0c;d&#xff0c;e&#xff0c;f&#xff0c;g&#xff0c;h&#xff0c;i&#xff0c;j&#xff0c;k}&#xff0c;且 E 中的边以如下的顺序处理:(d…

Leetcode 力扣117. 填充每个节点的下一个右侧节点指针 II (抖音号:708231408)

给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所有 next 指针都…

PCIE的吞吐量如何计算和记忆诀窍?

简介&#xff1a; PCIe标准中的性能参数有好几个&#xff0c;比如设备的带宽和吞吐量是多少&#xff1f;传输速率&#xff1f; 百度百科上&#xff0c;PCIE标准的传输速率与带宽对应表如下&#xff08;表中速率为单向速率&#xff09;。网上有些数据是双向的&#xff0c;性能数…

流程设计的基本步骤

背景 公司为什么要流程&#xff0c;已经有专门章节进行阐述&#xff1b; 什么是流程&#xff0c;已经有专门章节进行专门阐述&#xff1b; 那么接下来这个章节讨论&#xff0c;流程设计的基本步骤&#xff0c;那么谁来设计流程呢&#xff0c;让一个部门的员工来设计一份流程…

汽车行驶中是怎么保障轴瓦安全的?

汽车轴瓦是一种用于减少摩擦和支撑转动部件的关键零部件&#xff0c;通常用于发动机的曲轴、凸轮轴等转动部件上。主要作用是减少转动部件之间的摩擦&#xff0c;支撑和保护曲轴、凸轮轴等旋转部件&#xff0c;确保它们在高速旋转时的稳定性和耐用性。 在汽车轴瓦加工过程中&am…

HarmonyOS(36) DevEco Studio 配置debug和release

在android开发中可以在build.gradle来配置realease和debug,在HarmonyOS中可以通过build-profile.json5文件中通过buildModeSet配置&#xff1a; 在DevEco Studio 中可以通过下面来选择运行debug还是release&#xff1a; 我们可以通过BuildProfile.ets里面的静态变量获取当前…

企业中的绩效管理

背景 企业中为何需要绩效管理&#xff0c;企业绩效管理为何比较难&#xff0c;这在企业管理中是非常难&#xff0c;同样也是非常有价值的命题&#xff0c;那么首先应该对这个命题有清晰的认知&#xff0c;特别是要想明白为何企业需要绩效管理&#xff0c;应该先明白企业。 企…

C51学习归纳12 --- 外部中断、红外遥控

红外遥控是一个非常使用的技术&#xff0c;所以有必要单独讲一下。我们之前已经完成了电机调速的功能&#xff0c;现在我们讲红外控制和电机调速结合在一起&#xff0c;使用红外实现电机的调速。 为什么要采用外部中断&#xff0c;因为红外遥控的发送速率非常快&#xff0c;如果…

电商价格监测对于品牌渠道管控的重要性

当品牌开启经销渠道或涉足电商渠道时&#xff0c;必须着手进行线上线下价格监测。只有监控到电商价格&#xff0c;才能明晰出货后的商品历经多轮市场演绎后的实际价格&#xff0c;进而了解市场需求下的真实低价行为。借助力维网络开发的电商价格监测系统&#xff0c;品牌商能知…

【开发环境】PX4无人机实物使用视觉或运动捕捉系统进行位置估计

PX4无人机实物使用视觉或运动捕捉系统进行位置估计 PX4中关于外部位置信息的MAVLink话题参考坐标系EKF2调整配置参数调整EKF2_EV_DELAY参数 与ROS共同使用将OptiTrack MoCap系统提供的姿态数据导入ROSMotive MoCap软件的步骤将姿态数据导入ROS重新映射姿态数据 将姿态数据转发到…

【Three.js】知识梳理二十二:相机视角的平滑过渡与点击模型视角切换

在 Three.js 中&#xff0c;实现相机视角的平滑过渡和点击模型切换到查看模型视角是一个常见且有用的功能。这种效果不仅能提升用户体验&#xff0c;还能为场景互动添加更多的动态元素。本文将详细介绍如何在 Three.js 中实现这一功能。 1. 基本设置 首先&#xff0c;我们需要…