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

文章目录

  • 一、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 = xself.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.mzph.cn/web/30699.shtml

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

相关文章

大模型之-Seq2Seq介绍

大模型之-Seq2Seq介绍 1. Seq2Seq 模型概述 Seq2Seq&#xff08;Sequence to Sequence&#xff09;模型是一种用于处理序列数据的深度学习模型&#xff0c;常用于机器翻译、文本摘要和对话系统等任务。它的核心思想是将一个输入序列转换成一个输出序列。 Seq2Seq模型由两个主…

企业微信,机器人定时提醒

场景&#xff1a; 每天定时发送文字&#xff0c;提醒群成员事情&#xff0c;可以用机器人代替 人工提醒。 1&#xff09;在企业微信&#xff0c;创建机器人 2&#xff09;在腾讯轻联&#xff0c;创建流程&#xff0c;选择定时任务&#xff0c;执行操作&#xff08;企业微信机…

未登录也能知道你是谁?浏览器指纹了解一下!

引言 大多数人都遇到过这种场景&#xff0c;我在某个网站上浏览过的信息&#xff0c;但我并未登录&#xff0c;可是到了另一个网站发现被推送了类似的广告&#xff0c;这是为什么呢&#xff1f; 本文将介绍一种浏览器指纹的概念&#xff0c;以及如何利用它来判断浏览者身份。…

FIddler+Proxifer 实现PC客户端抓包详细教程

文章目录 前言1、Proxifer下载和配置1.1、下载Proxifer1.2、Proxifier配置 2、FIddler下载和配置2.1、FIddler下载2.2、FIddler配置 3、三、为什么抓不到有些应用程序的HTTP(s)的包&#xff1f; 前言 一般在浏览器场景下&#xff0c;我们可以利用Fiddler很好的完成抓包&#x…

计算机网络学习3

文章目录 以太网的MAC帧格式虚拟局域网VLAN概述虚拟局域网VLAN的实现机制以太网的发展802.11无线局域网的组成无线局域网的物理层无线局域网的数据链路层---使用CSMA/CD协议802.11无线局域网的MAC帧 网络层网络层概述网际协议IP和4.2.1异构网络互联IPv4地址及其编址方法概述IPv…

webpack处理样式资源04--webpack入门学习

处理样式资源 本章节学习使用 Webpack 如何处理 Css、Less、Sass、Scss、Styl 样式资源 介绍 Webpack 本身是不能识别样式资源的&#xff0c;所以我们需要借助 Loader 来帮助 Webpack 解析样式资源 我们找 Loader 都应该去官方文档中找到对应的 Loader&#xff0c;然后使用…

osgearth提示“simple.earth: file not handled”

在用vcpkg编译完osg和osgearth后&#xff0c;为了验证osgearth编译是否正确&#xff0c;进行测试&#xff0c;模型加载代码如下&#xff1a; root->addChild(osgDB::readNodeFile("simple.earth")); 此时以为是simple.earth路径的问题&#xff0c;遂改为以下代码…

Kithara实时定时器

管理定时器和实时定时器 目录 管理定时器和实时定时器管理时间数据通用定时器编程初始化计时器删除计时器停止/启动/调整定时器备注RealTimer 备注项目实例项目结构项目源码 示例更多&#xff1a; 管理时间数据 在 Kithara RealTime Suite 中&#xff0c;所有时间值均以 100 ns…

Pikachu靶场--文件上传

参考借鉴 Pikachu靶场之文件上传漏洞详解_皮卡丘文件上传漏洞-CSDN博客 文件上传漏洞&#xff1a;pikachu靶场中的文件上传漏洞通关_pikachu文件上传通关-CSDN博客 client check 在桌面新建一个文件夹&#xff0c;准备一个hello.php文件&#xff0c;文件写入如下代码 <?p…

产品3D模型在线展示

产品3D模型可以向潜在客户提供360度的观察角度&#xff0c;比平面图形的效果更好。快速实现产品3D模型的在线展示最简单的方法是使用老子云3D可视老子云3D可视化的模型内嵌特性&#xff0c;无需任何开发工作&#xff0c;5分钟就可以完成&#xff1a; 老子云的模型内嵌功能&…

[YOLOv10:注意力机制的轻量化创新,MLCA在目标检测中的卓越表现]

本文改进:一种轻量级的Mixed Local Channel Attention (MLCA)模块,该模块考虑通道信息和空间信息,并结合局部信息和全局信息以提高网络的表达效果。 1.YOLOv10介绍 论文:[https://arxiv.org/pdf/2405.14458] 代码: https://gitcode.com/THU-MIG/yolov10?utm_source=csdn…

二叉树的基础讲解

二叉树在遍历&#xff0c;查找&#xff0c;增删的效率上面都很高&#xff0c;是数据结构中很重要的&#xff0c;下面我们来基础的认识一下。(高级的本人还没学&#xff0c;下面的代码用伪代码或C语言写的)我会从树&#xff0c;树的一些专有名词&#xff0c;树的遍历&#xff0c…

ROM以及ROM与RAM对比

1.ROM ROM最原始的定义是“只读存储器”&#xff0c;一旦写入原始信息则不能更改。所以ROM通常用来存放固定不变的程序、常数和汉字字库&#xff0c;甚至用于操作系统的固化。它与随机存储器可共同作为主存的一部分&#xff0c;统一构成主存的地址域。 现在已经发展出了很多R…

《模拟联合国2.9—团队协作》

感谢上海财经大学持续的邀请&#xff0c;今天在阶梯教室举办的《模拟联合国2.0—团队协作》沙盘课程圆满结束。尽管场地的限制带来了一定的挑战&#xff0c;但得益于系统思考中“结构影响行为”的原则&#xff0c;我得以在不同场景中巧妙设计课程结构&#xff0c;极大地促进了大…

云顶森林的新守护者:大数据平台的智慧力量

在遥远的云顶之上&#xff0c;有一片生机盎然的森林&#xff0c;它不仅是动植物的家园&#xff0c;更是自然与人类和谐共生的典范。然而&#xff0c;如何在这片广袤的森林中实施高效、科学的管理&#xff0c;一直是一个摆在管理者面前的难题。幸运的是&#xff0c;随着科技的飞…

jQuery 样式操作

3.tab栏切换案例 实现效果&#xff1a; 案例分析&#xff1a; 核心代码&#xff1a; html结构&#xff1a; 4.jQuery类操作与className区别 1.操作css方法 jQuery可以使用Css方法来修改简单元素样式;可以操作类,修改多个样式。 参数只写属性名&#xff0c;则是返回属性值…

WordPress插件数据库批量替换内容工具插件

1、安装插件后&#xff0c;我们就可以在后台菜单看到工具操作界面 2、目前支持网站内容、标题、评论指定字符的快速替换 3、可以快速解决以往我们需要从MYSQL数据库命令替换的烦恼

DNF安卓分离仅是开始:游戏厂商积极布局自有渠道,市场变革在即

毫无征兆&#xff0c;DNF手游今天突然宣布从各大安卓平台下架。 《地下城与勇士:起源》运营团队于6月19日发布声明&#xff0c;指出因合约到期&#xff0c;游戏将不再上架部分安卓平台的应用商店。然而&#xff0c;这一事件并非完全无迹可循。 早在2021年初&#xff0c;华为游…

51单片机STC89C52RC——2.3 两个独立按键模拟控制LED流水灯方向

目的 按下K1键LED流水向左移动 按下K2键LED流水向右移动 一&#xff0c;STC单片机模块 二&#xff0c;独立按键 2.1 独立按键位置 2.2 独立按键电路图 这里要注意一个设计的bug P3_1 引脚对应是K1 P3_0 引脚对应是K2 要实现按一下点亮、再按一下熄灭&#xff0c;我们就需…

磁盘未格式化深度解析与应对策略

一、认识磁盘未格式化现象 在计算机世界中&#xff0c;磁盘未格式化是一个常见的故障现象。当系统提示磁盘未格式化时&#xff0c;意味着该磁盘或分区上的文件系统结构已损坏或丢失&#xff0c;导致计算机无法正确读取其中的数据。这种情况下&#xff0c;用户通常无法直接访问…