凸包(Convex Hull)

在图形学中,凸包是一个非常重要的概念。简明的说,在平面中给出N个点,找出一个由其中某些点作为顶点组成的凸多边形,恰好能围住所有的N个点。

这十分像是在一块木板上钉了N个钉子,然后用一根绷紧的橡皮筋它们都圈起来,这根橡皮筋的形状就是所谓的凸包。

计算凸包的一个著名算法是Graham Scan法,它的时间复杂度与所采用的排序算法时间复杂度相同,通常采用线性对数算法,因此为\( O\left(N\mathrm{log}\left(N\right)\right) \)。

1. 找到所有点\( P_{0,1,...,N-1} \)中最下方的点,记为\( P_{L} \);

2. 计算所有其他的点\( P_{i}\left(i\neq L\right) \) 与 \( P_{L} \)构成的向量\( \overrightarrow{P_{L}P_{i}} \)相对于水平轴的夹角。因为所有的点都在该\( P_{L} \)上方,因此向量的取值范围为\( \left(0, 180\right) \) ,所以可以用余切值代替角度值;

3. 对所有其他的点按照第2步算出的角度进行排序,且\( P_{L} \)为排序后的数组的第0位;

4. 从点\( P_{L} \)开始,依此连接每一个点(已经排序过),每连接一个点检测连线的走向是否是逆时针的,如果是则留下该点的前一个点,反之去除前一个点,使之与前面第二个点直接连接,继续这一检测,直到是逆时针或者所有点都被检测过为止。

判断三个点依此连成两条线段走向是否为逆时针,用这两条线段向量的叉积判断:叉积>0,逆时针;反之顺时针或者共线。

这里采用Qt 5.7实现了一个算法的演示程序,其中算法的部分如下(由于在Qt的坐标系中,y向下增长,因此在计算纵坐标差值时需要取相反数)。

void DisplayWidget::calConvexHull()
{
int size = m_points.size();
if (size < )
{
return;
} // First: find the lowest point
int maxY = ;
int indexOfLowest = -;
for (int i = ; i < size; i++)
{
if (m_points.at(i).y() > maxY)
{
maxY = m_points.at(i).y();
indexOfLowest = i;
}
} std::swap(*m_points.begin(), *(m_points.begin() + indexOfLowest));
QPoint &lowestPoint = *(m_points.begin()); // Second: calculate ctan(angles)
double *ctanAngles = new double[size];
for (int i = ; i < size; i++)
{
double deltaY = lowestPoint.y() - m_points.at(i).y() + DBL_EPSILON;
double deltaX = m_points.at(i).x() - lowestPoint.x();
ctanAngles[i] = deltaX / deltaY;
} // Third: Sort subscript
int *subscript = new int[size];
for (int i = ; i < size; i++)
{
subscript[i] = i;
}
std::sort(subscript + , subscript + size, [ctanAngles](int a1, int a2) { return ctanAngles[a2] < ctanAngles[a1]; }); // Fourth: Calculate convex hull
std::vector<QPoint> convexHullPoints;
convexHullPoints.push_back(*m_points.begin());
convexHullPoints.push_back(m_points.at(subscript[])); for (int i = ; i < size; i++)
{
convexHullPoints.push_back(m_points.at(subscript[i]));
while (convexHullPoints.size() > &&
!isAnticlockwise(*(convexHullPoints.end() - ), *(convexHullPoints.end() - ), *(convexHullPoints.end() - )))
{
*(convexHullPoints.end() - ) = *(convexHullPoints.end() - );
convexHullPoints.pop_back();
}
} m_convexHullPoints = std::move(convexHullPoints); delete[] ctanAngles;
delete[] subscript;
}

效果如下:

 

程序源码:http://files.cnblogs.com/files/HolyChen/ConvexHull.rar

凸包(Convex Hull)构造算法——Graham扫描法的更多相关文章

  1. opencv::凸包-Convex Hull

    概念介绍 什么是凸包(Convex Hull),在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部. 正式定义:包含点集合S中所有点的最小凸多边形称为凸包 Graham扫描算法 首先选 ...

  2. [POJ1113&POJ1696]凸包卷包裹算法和Graham扫描法应用各一例

    凸包的算法比较形象好理解 代码写起来也比较短 所以考前看一遍应该就没什么问题了..>_< POJ1113 刚开始并没有理解为什么要用凸包,心想如果贴着城堡走不是更好吗? 突然发现题目中有要 ...

  3. 计算几何 : 凸包学习笔记 --- Graham 扫描法

    凸包 (只针对二维平面内的凸包) 一.定义 简单的说,在一个二维平面内有n个点的集合S,现在要你选择一个点集C,C中的点构成一个凸多边形G,使得S集合的所有点要么在G内,要么在G上,并且保证这个凸多边 ...

  4. OpenCV入门之寻找图像的凸包(convex hull)

    介绍   凸包(Convex Hull)是一个计算几何(图形学)中的概念,它的严格的数学定义为:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包.   在图像处理过程中,我们 ...

  5. (模板)graham扫描法、andrew算法求凸包

    凸包算法讲解:Click Here 题目链接:https://vjudge.net/problem/POJ-1113 题意:简化下题意即求凸包的周长+2×PI×r. 思路:用graham求凸包,模板是 ...

  6. 凸包算法(Graham扫描法)详解

    先说下基础知识,不然不好理解后面的东西 两向量的X乘p1(x1,y1),p2(x2,y2) p1Xp2如果小于零则说明  p1在p2的逆时针方向 如果大于零则说明 p1在p2的顺时针方向 struct ...

  7. Graham 扫描法找凸包(convexHull)

    凸包定义 通俗的话来解释凸包:给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有的点  Graham扫描法 由最底的一点 \(p_1\) 开始(如果有多个这样的点, ...

  8. 关于graham扫描法求凸包的小记

    1.首先,凸包是啥: 若是在二维平面上,则一般的,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有的点. ───────────────────────────── ...

  9. Monotone Chain Convex Hull(单调链凸包)

    Monotone Chain Convex Hull(单调链凸包)算法伪代码: //输入:一个在平面上的点集P //点集 P 按 先x后y 的递增排序 //m 表示共a[i=0...m]个点,ans为 ...

随机推荐

  1. 【python】浅谈包

    python中的包可以理解为模块的集合.每个包也既可以为单包也可以有多个小包组成. Python中的package定义很简单,其层次结构与目录的层次结构相同,但是每个package必须包含一个__in ...

  2. nova分析(10)—— nova-rootwrap

    一.nova-rootwrap的作用 部署玩过openstack的都应该知道,它会生成一个nova用户来管理所有服务.nova身份在linux中属于普通用户级别,避免了一些需要root身份运行的操作, ...

  3. URL地址中的转义符

    如果在XML里面存储URL地址可能涉及到转义符的问题 WEB开发中通过问号(?)方式在浏览器地址栏中传值时.浏览器是通过“&”来区分问号后的参数个数的. 如果出现传值参数中带有“&”时 ...

  4. lucene 区分大小写 问题以及解决方案

    转自:http://blog.csdn.net/huaishu/article/details/8543236 本文介绍lucene区分大小的原因,和解决方案.关于lucene大小写敏感问题我总结一下 ...

  5. CK方程

    上文中,“到时间n为止进入任意一个特定的状态集合”应理解为“在时间n及之前进入的都算”. 只要进入了该状态集合,之后是否离开已经不重要了.这个可类比于“先赢若干局”的赌徒问题:即使在赢得若干局后继续赌 ...

  6. 【log】logback.xml

    <?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 控制台 - ...

  7. CSS3字体图标

    网址:http://icomoon.io/http://iconfont.cn/  阿里巴巴字体库 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...

  8. ServletContext(重要)

    一个项目只有一个ServletContext对象! 我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据! 这个对象在Tomcat启动时就创建,在Tomcat关 ...

  9. php 快速fork出指定个子进程

    $pids = array(); $child_pid = pcntl_fork(); if ($child_pid == -1) { throw new Exception( __METHOD__ ...

  10. SQL语句的Select部分只写必要的列

    如果Select部分包含不需要的列,这会强制DB2必须进入数据页来得到所请求的特定列,这就要求更多的I/O操作.另外,如果再对这个不需要的列进行排序,就需要创建和传递一个更大的排序文件,相应地会使排序 ...