C# | 凸包算法之Jarvis,寻找一组点的边界/轮廓

news/2024/7/21 4:25:06 标签: 算法, c#, 开发语言, 图像处理, .net

在这里插入图片描述

C#实现凸包算法之Jarvis

文章目录

  • C#实现凸包算法之Jarvis
    • 前言
    • 示例代码
    • 实现思路
    • 测试结果
    • 结束语

前言

这篇关于凸包算法的文章,本文使用C#和Jarvis算法来实现凸包算法
首先消除两个最基本的问题:

  1. 什么是凸包呢?
    凸包是一个包围一组点的凸多边形。凸多边形是指多边形中的每个内角都小于180度的多边形。
  2. 凸包算法有什么用呢?
    凸包算法的作用是找到这个凸多边形,并且使用最少的点来绘制出它的轮廓。凸包算法在计算机图形学、计算几何和机器学习等领域中有着广泛的应用。

示例代码

现在来看一下C#中的Jarvis算法是如何实现凸包算法的:

        /// <summary>
        /// 通过Jarvis算法获取围绕所有点的凸多边形的轮廓点<br/>
        /// </summary>
        /// <param name="points">点数组</param>
        /// <returns>轮廓点数组</returns>
        public static PointD[] GetConvexHullByJarvis(PointD[] points)
        {
            if (points.Length < 3)
            {
                throw new ArgumentException("凸包算法需要至少3个点");
            }

            List<PointD> pointList = new List<PointD>(points);

            // 找到最左边的点
            PointD leftmostPoint = pointList[0];
            for (int i = 1; i < pointList.Count; i++)
            {
                if (pointList[i].X < leftmostPoint.X)
                {
                    leftmostPoint = pointList[i];
                }
            }

            // 使用栈来存储凸包的点
            Stack<PointD> hull = new Stack<PointD>();
            PointD currentPoint = leftmostPoint;
            do
            {

                hull.Push(currentPoint);
                PointD endpoint = pointList[0];
                for (int i = 1; i < pointList.Count; i++)
                {
                    if (endpoint.Equals(currentPoint) || Cross(currentPoint, endpoint, pointList[i]) < 0)
                    {
                        endpoint = pointList[i];
                    }
                }
                currentPoint = endpoint;
                pointList.Remove(currentPoint);
            } while (!currentPoint.Equals(leftmostPoint));

            return hull.ToArray();
        }

上面代码中定义了一个名为GetConvexHullByJarvis的静态方法,该方法接受一个PointD类型的数组作为输入参数,并返回一个PointD类型的数组,表示围绕所有点的凸多边形的轮廓点。

补充一下,关于示例中使用到的Cross方法和PointD类型的源代码如下:

        /// <summary>
        /// 计算从 a 到 b 再到 c 的叉积
        /// </summary>
        /// <returns>叉积值</returns>
        private static double Cross(PointD a, PointD b, PointD c)
        {
            return (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
        }
    public struct PointD 
    {
        public PointD(double x, double y) 
        {
            X = x;
            Y = y;
        }

        public double X { get; set; }
        public double Y { get; set; }

        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }

            PointD other = (PointD)obj;
            return X.Equals(other.X) && Y.Equals(other.Y);
        }
    }

实现思路

  1. 找到最左边的点,将它放入凸包中;
  2. 找到在当前点的右侧且离当前点最远的点,将它放入凸包中;
  3. 重复这个过程,直到回到最左边的点,形成一个闭合的凸多边形。

这就是 Jarvis 算法的基本思路。

需要注意的是,当点集中存在大量共线的点时,Jarvis 算法的时间复杂度可能会退化到 O(n^2) 级别,因为需要不断地扫描点集中的点来找到下一个点。此外当点集中存在大量重复的点时,Jarvis 算法可能会陷入死循环,因此需要对点集进行去重操作。


测试结果

用于测试的数据点:

        static PointD[] points = new PointD[]
        {   
                new PointD(0, 0),
                new PointD(0, 10),
                new PointD(10, 10),
                new PointD(10, 0),
                new PointD(1, 0),

                new PointD(4, 3),
                new PointD(5, 2),
                new PointD(6, 5),
                new PointD(4, 9),
                new PointD(4, 2),

                new PointD(5, 1),
                new PointD(6, 5),
                new PointD(1, 3),
                new PointD(7, 2),
                new PointD(8, 2),

                new PointD(6, 7),
                new PointD(8, 5),
                new PointD(9, 3),
                new PointD(7, 8),
                new PointD(8, 9),
        };

测试代码如下:

        [TestMethod]
        public void GetConvexHullByJarvis()
        {
            Console.WriteLine("Jarvis 算法");
            PrintPoints(ConvexHull.GetConvexHullByJarvis(points));
        }

        private void PrintPoints(PointD[] points)
        {
            Console.WriteLine(points.Select(p => $"  ({p.X}, {p.Y})").Join("\r\n"));
        }

执行结果如下:
在这里插入图片描述


结束语

通过本章的代码可以轻松实现Jarvis算法并找到一组点最外侧的凸多边形。如果您觉得本文对您有所帮助,请不要吝啬您的点赞和评论,提供宝贵的反馈和建议,让更多的读者受益。


http://www.niftyadmin.cn/n/370011.html

相关文章

测牛学堂:自动化测试之python重点知识学习值数据类型转换和字符串重点

数字类型的强制类型转换 主要掌握两个&#xff0c;int和float,int转为整数&#xff0c;float转为小数 1强制类型转换&#xff0c;并不是说可以把任何的数据转成数字&#xff0c;而是符合转化条件的。比如带引号的数字。 比如&#xff0c;20’如果是一个字符串&#xff0c;那么…

多线程 -- 线程安全问题(3)

本篇重点: 总结线程安全问题的原因以及解决办法 目录 synchronized 加锁关键字join 和 synchronized 的区别volatile 关键字 在上一篇中我们介绍了Thread类的基本使用方法, 本篇将会介绍有关于线程的安全问题 线程不安全的原因: 抢占式执行(罪魁祸首, 万恶之源) 多个线程修改同…

SPA首屏加载速度慢的怎么解决?

SPA首屏加载速度慢的怎么解决&#xff1f; 加载慢的原因 网络延时问题资源文件体积是否过大资源是否重复发送请求去加载了加载脚本的时候&#xff0c;渲染内容堵塞了 解决方案 1.减小入口文件体积 常用的手段是路由懒加载&#xff0c;把不同路由对应的组件分割成不同的代码…

Ubuntu常见问题(issue)笔记

date: 2018-08-20 21:46:53 如何查看Ubuntu版本号&#xff1f; cat /etc/issueUbuntu 18.04.1 LTS本文的Linux版本如上。 安装后出现Bug soft lockup 也许是因为安装双系统的原因,导致Ubuntu安装后启动发现如下错误, 卡顿无比: kernel: xxx watchdog: Bug: soft lockup - CP…

新型智慧园区解决方案之园区通行管理设计

智慧园区作为现代化城市化建设的重要组成部分&#xff0c;已经成为未来城市的重要发展方向。在智慧园区的建设中&#xff0c;通行管理是一个十分重要的问题。为了解决这一问题&#xff0c;我们需要进行一系列的设计和方案的制定。 一、园区交通规划设计 园区交通规划设计是通…

remote-cloudflare-kv 在 Vercel 上使用 Cloudflare KV

最近我在做 Next.js 项目部署 Cloudflare Pages 时发现本地开发调试、登录鉴权等好多问题&#xff0c;所以又想要切回到 Vercel 中&#xff0c;便有了这么一个项目&#xff0c;可以在 Cloudflare 以外的环境上得到类似于 Worker Runtime 的 KV 使用体验。 废话不多说&#xff…

死锁的发生与避免

死锁的发生与避免 死锁是指两个或者多个进程在执行过程中&#xff0c;因争夺资源而造成的一种僵局&#xff0c;若无外力作用&#xff0c;它们都将无法推进下去。在计算机系统中&#xff0c;死锁是一种常见的问题&#xff0c;因此需要采取一些措施来避免死锁的发生。 死锁是一…

安全团队建设的几点思考

安全团队建设、氛围建设的几点思考&#xff1a; 1、确定团队内每个成员的一个基本角色和工作内容&#xff0c;强度和难度可跟职位挂钩&#xff0c;在此基础上可以相互协作、工作重叠&#xff0c;不过主责清晰明确&#xff0c;防止相互推诿。安全团队&#xff0c;相对其他产品团…