【学习笔记:计算几何基础1】 Convex Hull

Ahead

10.5.2018 - 10.6 2018
笔记摘自邓俊辉教授的MOOC 链接放在最后给= =
如果你没时间看视频就看文章,不然直接看视频好一点

First

计算几何的学习,凸包问题做引子。

凸包??(定义)

百度百科的定义:(不重要)
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
下图就是一个凸包,不过概念大概理解一下就可以了

Attention!

当点变化时,凸包上的极点(定义后面加,就是线上的点)可能增加可能减少

Second

先看下面一个例子

混合颜料

艺术家会混合颜料
由于计算机的颜色由RGB(这个不用解释吧? ) ,这里定义向量 C = (R,G)为颜色
例如 X = (10%,35%),Y = (16%,20%) 都是
那么如果用这两种颜色,我们能否调出我们需要的颜色?
例如 U = (12%,30%) 可以用 2份的X,一份的Y
即 U = 2X+Y
那么对于 V = (13%,22%) 是否可以? 都那么问了肯定是不行了
那么我们引入第三种颜色 Z = (7%,15%)
然后算一下可以发现是有解的 V = X+3Y+Z
那么我们能否找到一种方法,或者说,和如何用计算几何去操作呢?

Transform

对此,我们可以建立一个二维的欧式空间,不妨命名为颜色空间
同时设横轴为R比例,纵轴为G比例
讲以上的向量画在图中大概长这样

那么可以看见的是U点位于XY的线段上,V在XYZ所在的三角形内
如果向量还ok的话,可以看出U位于三等分点上,V对X,Y,Z的距离是3:3:1

结论

所以如果一个颜色能被调出来,那么这个颜色的向量一定处于所有拥有向量的多边形内。
证明什么的太难写了,向量代一下就ok了
那么可以发现的是,这个多边形就是一个闭包.(这个联系两幅图就能看出来)

Third

之前的都是引入,那么核心的问题是如何去构造凸包

概念

极点(Extremity Point):位于凸包的边上的点,即最终构成凸包的点 (EP表示)
特性: 对于极点而言,我们总能找到一条直线(可能多条),使得剩余的点都在直线的同一侧 且对于非极点一定不具有这样的性质 (即充要)

算法1 (EP)

根据调颜色的例子,我们去判断某个点是否落在一个三角形内,如果有,那么一定不是极点,否则一定是(类比一下)
所以我们也就是需要进行是否在三角形内的测试(In Triangle test)

伪代码

    初始化左右点为极点
    枚举三角形的三个点(p1,p2,p3)
    如果{p1,p2,p3,s} 在 In Triangle test 的返回值为真 
    那么 s 不为极点 

代码

for(int s=1;s<=n;++s) S[s].extreme = true; 
for(int i=1;i<=n;++i) 
  for(int j=i+1;j<=n;++i) 
    for(int k=j+1;k<=n;++k)  
      for(int s=1;s<=n;++s)
       {
           if(s==i || s==j || s==k || !S[s].extreme) continue; 
           if(InTriangle(S[i],S[j],S[k],S[s])) S[s].extreme=false; 
       } 

显然时间复杂度O(n^4) 很爆炸的效率

补坑1

In Triangle text 怎么写?
当然数学做肯定的是可以(但是精度误差什么的,指不定就WA了)
然而有一个更为巧妙的
画图可以发现 如果一个点位于三角形的内部,那么对于ccw(逆时针)遍历有向直线,该点总是落在左边 如果cw遍历 一定都落在右边

伪代码

 如果(i,j,s)(j,k,s),(k,i,s)的To-Left测试的真假性一致
 返回真
 否则
 返回假

代码

bool InTriangle(Point i,Point j,Point k,Point s) 
{
      bool a = ToLeft(i,j,s); 
      bool b = ToLeft(j,k,s); 
      bool c = ToLeft(k,i,s);  
      return ( a==b) && (b==c) 
}

所以这里又需要引进另一个测试 To-Left Text
在左边的测试

补坑2

To-Left测试 ??
这里牵扯到一个叉积的做法,这个牵扯到一系列,直接百度百科吧
这里直接结论化了 (还有行列式?? 似乎没什么用)
其实就是用叉积求面积 (左拐叉积大于0,右拐叉积小于0)
也就是2*s = a X b(a为边向量 b为起点到判定点s)

代码


int Area(Point a,Point b,Point c) 
{ 
  return a.x * b.y - a.y * b.x  + b.x * c.y - b.y * c.x + c.x * a.y - c.y * a.x;  
}

bool ToLeft(Point a,Point b,Point c) 
{ 
   return Area(a,b,c) > 0; 
}

这种做法能避免精度问题
有点多了,开个新帖子.
代码可能有错= =(应该不会吧)
直接敲上去的
计算几何看这里=TAT

posted @ 2018-10-05 21:17  PiCaHor  阅读(645)  评论(0编辑  收藏  举报