凸包问题——分治法 Python实现

news2024/11/16 19:57:47

凸包问题。给定平面上n个点,从中找出一个最小点集,使得该点集所组成的凸多边形包围所有的n个点。基于分治策略,设计一个求解凸包问题的算法。实现该算法并测试。

分治算法思路:

  1. 如果点集中的点数小于等于3,可以直接返回这些点作为凸包,因为凸包至少包括3个点。
  2. 将点集排序,用最左点 P 1 P_1 P1和最右点 P n P_n Pn连成的直线分成两个子集:上包和下包。
  3. 获得距离直线最远的点 P m a x P_{max} Pmax
  4. 作直线 P 1 P m a x 和 P n P m a x P_1 P_{max}和P_n P_{max} P1PmaxPnPmax,分别对左侧和右侧求上包
  5. 递归地对上包进行凸包计算。
  6. 下包同理
    在这里插入图片描述

在这里插入图片描述

两点构成的直线函数为:
y − y 1 y 2 − y 1 = x − x 1 x 2 − x 1 \frac{y-y_1}{y_2-y_1} = \frac{x-x_1}{x_2-x_1} y2y1yy1=x2x1xx1
代入点到直线的距离公式,得距离为:
∣ ( x 2 − x 1 ) y 3 − ( y 2 − y 1 ) x 3 − ( x 2 − x 1 ) y 1 + ( y 2 − y 1 ) x 1 ∣ ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 \frac{|(x_2-x_1)y_3-(y_2-y_1)x_3-(x_2-x_1)y_1+(y_2-y_1)x_1|}{\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}} (x2x1)2+(y2y1)2 (x2x1)y3(y2y1)x3(x2x1)y1+(y2y1)x1
因为我们需要求距离最远,因此只需要计算
∣ ( x 2 − x 1 ) y 3 − ( y 2 − y 1 ) x 3 − ( x 2 − x 1 ) y 1 + ( y 2 − y 1 ) x 1 ∣ = ∣ x 1 ∗ y 2 + x 3 ∗ y 1 + x 2 ∗ y 3 − x 3 ∗ y 2 − x 2 ∗ y 1 − x 1 ∗ y 3 ∣ |(x_2-x_1)y_3-(y_2-y_1)x_3-(x_2-x_1)y_1+(y_2-y_1)x_1| \\ =|x1 * y2 + x3 * y1 + x2 * y3 - x3 * y2 - x2 * y1 - x1 * y3| (x2x1)y3(y2y1)x3(x2x1)y1+(y2y1)x1=x1y2+x3y1+x2y3x3y2x2y1x1y3∣
最大即可。

代码如下:

import random
import matplotlib.pyplot as plt
from typing import List


# slot是为了减少内存开销, lt是为了排序, iter是为了方便取值
class Point:
    __slots__ = ['x', 'y']
    def __init__(self, x:float, y:float) -> None:
        self.x = x
        self.y = y

    def __lt__(self, other):
        if self.x == other.x:
            return self.y < other.y
        return self.x < other.x

    def __iter__(self):
        yield self.x
        yield self.y

def cacl_dis(a:Point, b:Point, c:Point) -> float:
    x1, y1 = a
    x2, y2 = b
    x3, y3 = c
    return x1 * y2 + x3 * y1 + x2 * y3 - x3 * y2 - x2 * y1 - x1 * y3


def border_point_up(left_point:Point, right_point:Point, lists:List[Point], border_points:List[Point]) -> None:
    dis_max = 0
    max_point = None
    for item in lists:
        if item == left_point or item == right_point:
            continue
        else:
            dis = cacl_dis(left_point, right_point, item)
            if dis > dis_max:
                max_point = item
                dis_max = dis

    if dis_max != 0:
        border_points.append(max_point)
        border_point_up(left_point, max_point, lists, border_points)
        border_point_up(max_point, right_point, lists, border_points)


def border_point_down(left_point:Point, right_point:Point, lists:List[Point], border_points:List[Point]) -> None:
    dis_max = 0
    max_point = ()
    for item in lists:
        if item == left_point or item == right_point:
            continue
        else:
            dis = cacl_dis(left_point, right_point, item)
            if dis < dis_max:
                max_point = item
                dis_max = dis

    if dis_max != 0:
        border_points.append(max_point)
        border_point_down(left_point, max_point, lists, border_points)
        border_point_down(max_point, right_point, lists, border_points)


def order_border(lists: List[Point]) -> List[Point]:
    lists.sort()
    first_x, first_y = lists[0]  # 最左边的点
    last_x, last_y = lists[-1]  # 最右边的点
    list_border_up = []  # 上半边界
    for item in lists:
        x, y = item
        if y > max(first_y, last_y):
            list_border_up.append(item)
        if min(first_y, last_y) < y < max(first_y, last_y):
            if cacl_dis(lists[0], lists[-1], item) > 0:
                list_border_up.append(item)
            else:
                continue
    list_border_down = [_ for _ in lists if _ not in list_border_up]  # 下半边界
    list_end = list_border_up + list_border_down[::-1]  # 最终顺时针输出的边界点
    return list_end


def draw(list_points:List[Point], list_borders:List[Point]) -> None:
    list_all_x = []
    list_all_y = []
    for item in list_points:
        a, b = item
        list_all_x.append(a)
        list_all_y.append(b)
    list_borders.append(list_borders[0])
    plt.scatter(list_all_x, list_all_y)

    for i in range(len(list_borders) - 1):
        one_, oneI = list_borders[i]
        two_, twoI = list_borders[i + 1]
        plt.plot([one_, two_], [oneI, twoI], color='red')
        plt.scatter([one_, two_], [oneI, twoI], color='red')

    plt.show()

# 生成随机点集
def generate_random_points(n:int, xmin:float, xmax:float, ymin:float, ymax:float)->List[Point]:
    return [Point(random.uniform(xmin, xmax), random.uniform(ymin, ymax)) for _ in range(n)]


if __name__ == "__main__":
    n = 100
    x_min, x_max = 0, 100
    y_min, y_max = 0, 100
    list_points = generate_random_points(n, x_min, x_max, y_min, y_max)
    list_points.sort()
    border_points = []  # 边界点集
    border_point_up(list_points[0], list_points[-1], list_points, border_points)  # 上边界点集
    border_point_down(list_points[0], list_points[-1], list_points, border_points)  # 下边界点集
    border_points.append(list_points[0])
    border_points.append(list_points[-1])
    draw(list_points, order_border(border_points))

这里随机初始化100个点来测试代码,并且将结果通过matplotlib展示出来:
在这里插入图片描述

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

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

相关文章

管理类联考——数学——汇总篇——知识点突破——代数——函数、方程——记忆

文章目录 考点记忆/考点汇总——按大纲 整体局部 本篇思路&#xff1a;根据各方的资料&#xff0c;比如名师的资料&#xff0c;按大纲或者其他方式&#xff0c;收集/汇总考点&#xff0c;即需记忆点&#xff0c;在通过整体的记忆法&#xff0c;比如整体信息很多&#xff0c;通常…

hdlbits系列verilog解答(always块if语句)-31

文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 if 语句通常创建一个 2 对 1 多路复用器,如果条件为 true,则选择一个输入,如果条件为 false,则选择另一个输入。 always @(*) begin if (condition) begin out = x; end else begin out = y; end end 这等…

【算法专题】双指针—有效三角形的个数

一、题目解析 题目链接&#xff1a;有效三角形的个数 我们知道想要组成一个三角形那么其任意两边之和必定大于第三边&#xff0c;即 但是如果我们知道这三条边的大小顺序&#xff0c;那么只需判断一次即可&#xff0c;假设c是最大的那条边&#xff0c;那么不等式②和③不用判断…

Rust编程基础之函数和表达式

1.Rust函数 在之前的文章中,我们已经见到了一个函数:main函数, 它是很多程序的入口点。也见过 fn 关键字&#xff0c;它用来声明新函数。 Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中&#xff0c;所有字母都是小写并使用下划线分隔单词。这是一个包…

LEEDCODE 709转换成小写字母

class Solution { public:string toLowerCase(string s) {int len s.length();string a "";for(int i 0; i < len; i){if(s[i] > 65 && s[i] < 90){a (s[i] 32);}elsea s[i];}// cout<<a<<endl;return a;} };

行业追踪,2023-11-02

自动复盘 2023-11-02 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

Amazon Generative AI 新世界 | 基于 Amazon 扩散模型原理的代码实践之采样篇

以前通过论文介绍 Amazon 生成式 AI 和大语言模型&#xff08;LLMs&#xff09;的主要原理之外&#xff0c;在代码实践环节主要还是局限于是引入预训练模型、在预训练模型基础上做微调、使用 API 等等。很多开发人员觉得还不过瘾&#xff0c;希望内容可以更加深入。因此&#x…

uniapp循环对象列表---点击列表切换选中不同状态

目录 源码图片最后 源码 <template><view><ul><li v-for"(item, index) in list" click"toggleSelection(index)" :class"{selected: selectedIndex index}">{{ item }}<view :class"{selected: selectedInde…

系列五、映射文件xxxMapper.xml

一、概述 mapper映射文件是mybatis中最重要的部分&#xff0c;涉及到的细节也非常多。 1.1、parameterType 表示输入参数的类型。例如&#xff1a; <select id"getUserById" parameterType"integer" resultType"org.star.entity.model.UserDO&…

Mysql数据库基础知识补充

sql知识补充 一.数据库的操作1.显示当前数据库2.创建数据库3.使用数据库4.删除数据库 二.常用数据类型1.数值类型2.字符串类型3.日期类型 三.表的操作1.查看表结构2.创建表3.删除表 一.数据库的操作 1.显示当前数据库 2.创建数据库 3.使用数据库 4.删除数据库 二.常用数据类型…

netty实战-手写通信框架

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用&#xff0c;它的主要功能如下&#xff1a; 基于 Netty 的 NIO 通信框架&#xff0c;提供高性能的异步通信能力&#xff1b; 提供消息的编解码框架&#xff0c;可以实现 POJO 的序列化和反序…

轻松部署Swagger Editor:安装Docker并实现远程访问编辑API文档

文章目录 Swagger Editor本地接口文档公网远程访问1. 部署Swagger Editor2. Linux安装Cpolar3. 配置Swagger Editor公网地址4. 远程访问Swagger Editor5. 固定Swagger Editor公网地址 Swagger Editor本地接口文档公网远程访问 Swagger Editor是一个用于编写OpenAPI规范的开源编…

Java算法:选择排序

一、选择排序 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的 排序算法 。 工作原理&#xff1a;第一次从待排序的 数据元素 中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;然后再从剩余的未排序元素中寻…

Android Snackbar

1.Snackbar Snackbar是Material Design中的一个控件&#xff0c;用来代替Toast。Snackbar是一个类似Toast的快速弹出消息提示的控件。Snackbar在显示上比Toast丰富&#xff0c;而且提供了用户交互的接口。 ①默认情况下&#xff0c;Snackbar显示在屏幕底部&#xff0c;它出现…

蓝鹏测控测宽仪系列又添一员大将——双目测宽仪

轧钢过程中钢板的宽度是一个重要的参数&#xff0c;它直接决定了成材率。同时&#xff0c;随着高新科技越来越广泛的应用到工程实际中&#xff0c;许多控制系统需要钢板实时宽度值作为模型参数。 当前&#xff0c;相当一部分宽厚板厂还在采用人工检测的方法&#xff0c;检测环境…

一文5个步骤用Jmeter做接口测试!

说实话&#xff0c;在游戏测试领域&#xff0c;做接口测试的并不多&#xff0c;做的好的更是寥寥无几&#xff08;请大家不要喷游戏测试比较low&#xff0c;行业现状如此而已&#xff09;。绝大部分游戏测试人员都是以功能测试为主&#xff0c;偶尔做做性能测试和压力测试已经很…

STM32F103C8T6第二天:认识STM32 标准库与HAL库 GPIO口 推挽输出与开漏输出

1. 课程概述&#xff08;297.1&#xff09; 课程要求&#xff1a;C语言熟练&#xff0c;提前学完 C51 2. 开发软件Keil5的安装&#xff08;298.2&#xff09; 开发环境的安装 编程语言&#xff1a;C语言需要安装的软件有两个&#xff1a;Keil5 和 STM32CubeMX Keil5 的安装…

Fiddler实现 HTTP 网络抓包

文章目录 前言Fiddler 是什么下载 Fiddler1. 官网下载 Fiddler Classic2. 安装 Fiddler Classic3. 打开 Fiddler Classic 前言 前面我们简单地学习了关于应用层——自定义协议的知识&#xff0c;但是这都只是自定义协议&#xff0c;在实际生活中自定义协议用的还是占少数的&am…

终于有人把VMware虚拟机三种网络模式讲清楚了!

你们好&#xff0c;我的网工朋友。 前段时间VMware更新了&#xff0c;你用上最新版了吗&#xff1f; 有几个网工朋友留言说&#xff0c;在操作中遇到过各种各样的问题。比如说由于公司服务器重启导致出现下面的问题&#xff1a; 在Xshell里连接虚拟机映射时连接失败&#xf…

【Java|golang】2103. 环和杆---位运算

总计有 n 个环&#xff0c;环的颜色可以是红、绿、蓝中的一种。这些环分别穿在 10 根编号为 0 到 9 的杆上。 给你一个长度为 2n 的字符串 rings &#xff0c;表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 &#xff0c;用于描述每个环&#xff1a; 第 …