精华内容
下载资源
问答
  • 平面点集与多元函数

    2020-09-01 20:13:39
    平面点集与多元函数一 平面点集平面点集&的圆邻域与 &的方邻域内点、外点、界点聚点R2上的完备性定理二元函数n原函数 一 平面点集 平面点集 &的圆邻域与 &的方邻域 内点、外点、界点 聚点 R2...

    一 平面点集

    平面点集

    在这里插入图片描述

    &的圆邻域与 &的方邻域

    在这里插入图片描述
    在这里插入图片描述

    内点、外点、界点

    在这里插入图片描述

    聚点

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    开集、闭集

    在这里插入图片描述

    开域、闭域

    在这里插入图片描述
    在这里插入图片描述

    R2上的完备性定理

    定义1

    在这里插入图片描述

    定理16.1 柯西准则

    在这里插入图片描述
    在这里插入图片描述

    定理16.2 闭域套定理

    在这里插入图片描述

    推论

    在这里插入图片描述
    在这里插入图片描述

    定理16.3 聚点定理

    在这里插入图片描述

    定理16.3’

    在这里插入图片描述

    定理16.4 有限覆盖定理

    在这里插入图片描述

    二元函数

    在这里插入图片描述

    定义2

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    有界函数与无界函数

    在这里插入图片描述

    n原函数

    在这里插入图片描述

    展开全文
  • 问题:求平面点集S内点对的最远距离... 1 解答:... 1 一. 凸包... 1 二. 旋转卡壳算法... 2 三. 算法总复杂度... 8 四. C++实现代码... 8 五. 测试... 12   问题:求平面点集S内点对的最远距离 ...
      
    

    目录

    问题:求平面点集S内点对的最远距离... 1

    解答:... 1

    一.         凸包... 1

    二.         旋转卡壳算法... 2

    三.         算法总复杂度... 8

    四.         C++实现代码... 8

    五.         测试... 12

     

    问题:求平面点集S内点对的最远距离

    解答:

        其实这个问题的解法就是先求这个点集的凸包,然后运用旋转卡壳算法求此凸包的直径,便得到最远距离(即为此凸包直径)。

    一.凸包

    点集Q的凸包(convex hull):是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。右图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。如图1所示。

                                      图1

    平面凸包求法:Graham's Scan法求解凸包问题

    问题:给定平面上的二维点集,求解其凸包。

      过程 :

    1.  选取基点: 在所有点中选取y坐标最小的一点H,当作基点。如果存在多个点的y坐标都为最小值,则选取x坐标最小的一点。坐标相同的点应排除。

    2.      排序:按照其它各点p和基点构成的向量<H,p>与x轴的夹角进行排序,依据夹角从小到大进行排序,如果夹角相等,则按照与基点的距离从小到大进行排序。实现中无需求得夹角,只需根据向量的内积公式求出向量的模即可。

    3.  去掉凹进去的点:按照排序后各点得顺序进行扫描,即逆时针扫描。基准点在最前面,设这些点为P[0]..P[n-1];建立一个栈,初始时P[0]、P[1]、P[2]进栈,对于P[3..n-1]的每个点,若栈顶的两个点与它不构成“向左转”的关系(即栈顶点是凹进去的点),则将栈顶的点出栈,直至没有点需要出栈以后将当前点进栈。所有点处理完之后栈中保存的点就是凸包上的点了。如何判断A、B、C构成的关系不是向左转呢?如果b-a与c-a的叉乘小于0就不是。a与b的叉乘就是a.x*b.y-a.y*b.x。

     

      复杂度 :

    由于在扫描凸包前要进行排序,因此时间复杂度至少为快速排序的O(nlogn)。后面的扫描过程复杂度为O(n),因此整个算法的复杂度为O(nlogn)。

    二.旋转卡壳算法

    当点集的凸包求出之后,便运用旋转卡壳算法求此凸包的直径。

    有二种卡壳方法,第一种是卡住两点,如图2所示。 第二种是卡住一条边和一个点,如图3所示。由于实现中卡住两点的情况不好处理,所以我们通常关注第二种情况。

    图2

    图3

    在第二种情况中,我们可以看到,一个对踵点和对应边之间的距离比其他点要大,也就是一个对踵点和对应边所形成的三角形是最大的。如图4所示。下面我们会据此得到对踵点的简化求法。

    图4

    /*******************************************************************/ 

    /* 函数功能:卡壳简化算法                             */ 

    /*******************************************************************/ 

    double RotatingCalipers(vector<Point> result, int n)  

    {  

        int j = 1;  

        double maxLength = 0.0;//存储最大值  

        result[n] = result[0];

        for(int i = 0; i<n; i++)  

        {  

            while(CrossProduct(result[i+1], result[j+1], result[i]) > CrossProduct(result[i+1], result[j], result[i]))  

                j = (j+1)%n;  

            maxLength = max(maxLength, max(Distance(result[i], result[j]), Distance(result[i+1], result[j+1])));              

        }  

        return maxLength;   

    }  

    上面就是简化的旋转卡壳寻找对踵点的过程。

    其中叉积函数CrossProduct(A, B, C) 返回BA到CA的二维定义下的叉积。这里主要用到了叉积求三角形面积的功能。

    对于判断凹凸的实现,基本都采用向量的叉积而不是去计算角度。根据右手法则,逆时针旋转为正,顺时针旋转为负,因此可用以下函数判别:

    // 求向量p2-p1,p3-p1的叉积 

    int CrossProduct(const Point &pre, const Point &cur, const Point &next)//pre是上一个点,cur是当前点,next是将要选择的点   

    {  

     

         int x1 = cur.x - pre.x;  

         int y1 = cur.y - pre.y;  

              int x2 = next.x - pre.x;  

              int y2 =  next.y - pre.y;  

         return (x1*y2 - y1*x2);>0是满足凸包的点  

    }  

     

    /*******************************************************************/ 

    //凹凸测试

    /*******************************************************************/ 

    bool LeftTurnTest(const Point &p1,const Point &p2,const Point &p3)

    {

             int product = CrossProduct(p1,p2,p3);

             if(product>0)

                       return true;

             //p2点必须在p1-p3线段上面

             if(product==0)

                       return (p3.x-p2.x)*(p2.x-p1.x)>0||(p3.y-p2.y)*(p2.y-p1.y)>0;

             return false;

    }

    我们对于一条对应边<CHi,Chi+1>求出距离这条边最远的点CHj,则由上面第二种情况可知 CHi和CH j 为一对对踵点,这样让CHj绕行凸包一周即可得到所有的对踵点。如图5所示。

    图5

    接下来考虑,如何得到距离每条对应边的的最远点呢?

    稍加分析,我们可以发现凸包上的点依次与对应边产生的距离成单峰函数。具体证明可以从凸包定义入手,可以利用反证法解决。如前面图4所示。

    这样我们再找到一个点,使下一个点的距离小于当前的点时就可以停止了。而且随着对应边的旋转,最远点也只会顺着这个方向旋转,我们可以从上一次的对踵点开始继续寻找这一次的。

    由于内层while循环的执行次数取决于j增加次数,j最多增加O(n)次,所以求出所有对踵点的时间复杂度为O(n)。

    意外问题:

    1.“倒刺”:

    测试发现,当点的数量比较多的时候(>1000)就容易出现“倒刺”现象,如图6所示。

    图6

    “倒刺”都有一定的规律—很尖。“尖”说明向量方向问题,也就是说,当叉积为0的时候,会有两种情况:同向和对向。如图7所示。

    图7

        对向的时候是“尖刺”,并不满足凸包条件,同向的时候是平边,应该保留,否则最远点对会出现遗漏。

    2. “包外点”:

    当发现并解决了“倒刺”问题的时候,又发现了居然有些点在凸包外,如图8所示。

    图8

    原来在第二步“排序”的时候,如果出现角度一样的点怎么弄?如序列:B -A - C三个点,B - A向量和 C - A和x轴的夹角是一致的,那么可能出现“先长后短”和“先短后长”两种情况,前者就是“包外点”的罪魁祸首。如图9所示。所以在排序时,当出现与X轴夹角相等时,按照与基点H的距离从小到大排序。

    图9

     

    三.  算法总复杂度

    Graham's Scan法求解凸包为O(nlogn),旋转卡壳算法O(n),所以总的复杂度为O(nlogn)。

    四.  C++实现代码

    #include "stdafx.h"

    #include <iostream>  

    #include <vector>  

    #include <stack>  

    #include <algorithm>  

    #include <iterator>  

    #include <cmath>  

    using namespace std; 

    //点类

    class Point  

    {  

    public:  

        Point(){}  

        Point(int m_x, int m_y):x(m_x),y(m_y){}  

        int x;  

        int y;  

        friend ostream& operator<< (ostream &out, const Point &point);  

    }; 

    //用来存储点集

    vector<Point> vec; 

     

    /************************************************************************/

    //交换二个点

    /************************************************************************/

    void swap(Point *p1,Point *p2)

    {

             Point temp = *p1;

             *p1 = *p2;

             *p2 = temp;

    }

    /************************************************************************/

    //函数功能:寻找基点

    /************************************************************************/

    int BasicPoint(vector<Point> vec)

    {

             int p = 0;

             for(int i = 1;i<(int)vec.size(); ++i)

             {

                       if(vec[i].y<vec[p].y||vec[i].y==vec[p].y&&vec[i].x<vec[p].x)

                                p = i;

             }

             return p;

    }

    /************************************************************************/

    //函数功能:返回二者较大值

    /************************************************************************/

    double max( double len1,double len2)

    {

             return len1>len2?len1:len2;

    }

     

    /************************************************************************/ 

    /* 函数功能:求两个向量的叉积  */ 

    /************************************************************************/ 

    int CrossProduct(const Point &pre, const Point &cur, const Point &next)//pre是上一个点,cur是当前点,next是将要选择的点   

    {  

     

         int x1 = cur.x - pre.x;  

         int y1 = cur.y - pre.y;  

              int x2 = next.x - pre.x;  

              int y2 =  next.y - pre.y;  

        return (x1*y2 - y1*x2); //>0是满足凸包的点  

    }  

     

    /************************************************************************/ 

    /* 函数功能:求两点间的距离的平方                       */ 

    /************************************************************************/ 

    double Distance(const Point &point1, const Point &point2)  

    {  

        return (point1.x - point2.x)*(point1.x - point2.x) + (point1.y - point2.y)*(point1.y - point2.y);  

    }  

    /*******************************************************************/ 

    /* 函数功能:比较两个点,依据夹角从小到大进行排序,如果夹角相等,则按照与基点的距离从小到大进行排序。  

    /*******************************************************************/ 

    bool Cmp(const Point &left, const Point &right)  

    {  

              int product = CrossProduct(vec[0],left,right);

              if(product != 0)

                        return product>0;

              double d1= Distance(left,vec[0]);

              double d2 = Distance(right,vec[0]);

              return d1<d2;

    }  

    /*******************************************************************/ 

    //函数功能:凹凸测试

    /*******************************************************************/ 

    bool LeftTurnTest(const Point &p1,const Point &p2,const Point &p3)

    {

             int product = CrossProduct(p1,p2,p3);

             if(product>0)

                       return true;

             //p2点必须在p1-p3线段上面

             if(product==0)

                       return (p3.x-p2.x)*(p2.x-p1.x)>0||(p3.y-p2.y)*(p2.y-p1.y)>0;

             return false;

    }

    /*******************************************************************/ 

    //函数功能:输出流重定义

    /*******************************************************************/ 

    ostream& operator<< (ostream &out, const Point &point)  

    {  

        out<<"("<<point.x<<","<<point.y<<")";  

        return out;  

    }  

     

    /************************************************************************/ 

    /* 函数功能:获取凸包 

       参数vec存放输入的点,result存放凸包上的点*/ 

    /************************************************************************/ 

    void GetConvexHull(vector<Point> vec, vector<Point> &result)  

    {  

              //寻找基点,并把基点与vec[0]交换位置

              int p = BasicPoint(vec);

              swap(&vec[0],&vec[p]);

              //除基点外,对于其余的点,按照与基点的连线与x轴的夹角从小到大进行排序,

              //如果夹角相等,则按照与基点的距离从小到大进行排序

        sort(vec.begin()+1, vec.end(), Cmp);   

     

        int size = vec.size();  

        if(size < 3)  

        {  

            copy(vec.begin(), vec.end(), back_inserter(result));  

        }  

        else 

        {  

            result.push_back(vec.at(0));  

            result.push_back(vec.at(1));  

            result.push_back(vec.at(2));  

            int top = 2;  

            for(int i=3; i<size; i++)  

            {  

                while((top>0) && (!LeftTurnTest(result.at(top-1), result.at(top), vec.at(i))) ) 

                {  

                    result.pop_back();  

                    top--;  

                }  

                result.push_back(vec.at(i));  

                top++;  

            }  

        }  

    }  

    /************************************************************************/ 

    /* 函数功能:简化的旋转卡壳算法                             */ 

    /************************************************************************/ 

    double RotatingCalipers(vector<Point> result, int n)  

    {  

        int j = 1;  

        double maxLength = 0.0;//存储最大值  

        result[n] = result[0];

        for(int i = 0; i<n; i++)  

        {  

            while(CrossProduct(result[i+1], result[j+1], result[i]) > CrossProduct(result[i+1], result[j], result[i]))  

                j = (j+1)%n;  

            maxLength = max(maxLength, max(Distance(result[i], result[j]), Distance(result[i+1], result[j+1])));              

        }  

        return maxLength;   

    }  

     

     

    int _tmain(int argc, _TCHAR* argv[])

    {

            

             //产生N个随机点

        const int N = 20;  

        for(int i=0; i<N; i++)  

        {  

            vec.push_back(Point(rand()%20, rand()%20));  

        }  

        cout<<"平面上的点:"<<endl;  

        copy(vec.begin(), vec.end(), ostream_iterator<Point>(cout, "\n"));  

        cout<<endl;  

        vector<Point> result;

             //求凸包

        GetConvexHull(vec, result);  

        

             //打印凸包上的点

        cout<<"凸包上的点:"<<endl;  

        copy(result.begin(), result.end(), ostream_iterator<Point>(cout, " "));  

        cout<<endl;  

        double distace = RotatingCalipers(result, result.size()-1);  

        cout<<"最远距离为:"<<sqrt(double(distace))<<endl;

             system("pause");

             return 0;

    }

     

    五.  测试

    平面上的点:

    (7,1)

    (0,14)

    (4,9)

    (18,18)

    (4,2)

    (5,5)

    (7,1)

    (11,1)

    (2,15)

    (16,7)

    (4,11)

    (13,2)

    (2,12)

    (16,1)

    (15,18)

    (6,7)

    (18,11)

    (12,9)

    (19,7)

    (14,15)

     

    凸包上的点:

    (7,1) (7,1) (11,1) (16,1) (19,7) (18,18) (15,18) (2,15) (0,14) (4,2)

    最远距离为:20.2485

    请按任意键继续. . .

    展开全文
  • 平面点集和多元函数知识总结

    千次阅读 2020-05-08 23:03:27
    内点:存在某领域U(A),U(A)属于E 外点:存在某领域U(A),U(A)与E相交为空集 界点:任意领域U(A),U(A)和E...E的聚点都属于E(也即界点都属于E,因为内点一定属于E,但是这个定义好证,只要说明随便一个点是聚点,且满

    内点:存在某领域U(A),U(A)属于E

    外点:存在某领域U(A),U(A)与E相交为空集

    界点:任意领域U(A),U(A)和E以及E的补集相交不为空集

    三种关系类比为区间内,区间外和区间的上下确界
    内点一定属于E,外点一定不属于E,界点可能属于E

    聚点定义:
    (1)任何空心领域中有E中的点
    (2)任何领域包含E的无数个点
    内点一定是聚点,外点一定不是界点,界点可能是聚点

    开集:
    每一点都是内点

    闭集:
    E的聚点都属于E(也即界点都属于E,因为内点一定属于E,但是这个定义好证,只要说明随便一个点是聚点,且满足E的条件就可以了)

    开域:非空且连通的开集,闭域:开域以及所有界点,

    点列的收敛:
    (1)任意e>0,存在N,任意n>N时, p n ∈ U ( p 0 ) p_n\in U(p_0) pnU(p0)
    也就是点列在无穷多项后都落在某个点领域中
    (2)柯西准则,点列收敛的充要条件是很多项以后两点距离无限小

    闭域套定理

    聚点定理

    有界无限点列必有收敛子列

    有限覆盖定理。

    我觉得以上不怎么考,就随便略过。

    二元函数:z与f(x,y)对应,注意(u,v)和f(x,y)对应叫做向量函数
    注意数量函数(二元函数)和向量函数的区别,向量函数相当于多个二元函数。

    1.如果问是不是闭集或者开集:
    如果是区域,首先看边界有没有被取到,
    如果是一条线,看看有没有点没被取到,有点没被取到大概率是开集
    如果是离散的点集,那也是闭集
    2.以 p 0 p_0 p0为极限构造趋于它的点列,第一个点,p1任取,第二个点开始,在U( p 0 p_0 p0,e)中找一个点,其中 e=min{ p 1 − p 0 , . . . . , p i − p i − 1 , 1 n p1-p0,....,p_i-p_{i-1},\frac{1}{n} p1p0,....,pipi1,n1}。就是距离越来越近,但是又有一个1/n来控制不能间距太大
    3.点列的收敛,是两点的距离小于e
    4.补集是开集,原集是闭集:反证法,原集若非闭,则存在一个原集的聚点A不属于原集,属于补集,补集是开集,所以A领域在补集中,聚点的领域里没有原集的点,矛盾
    总之要反复优先利用开闭集的性质,比如A属于补集,然后利用补集是开集,而不是先利用聚点的性质
    5.有关差集A-B就转化成B补集和A的交

    展开全文
  • 平面点集的凸包算法

    千次阅读 2020-02-07 17:31:23
    平面点集的凸包算法 参考翻译自Dan Sunday的文章The Convex hull of a point set 凸包计算是计算集合中的一个经典问题。这一问题有许多变种,其中最普遍的形式是计算平面离散点集的最小凸集(称为“凸包”)。这一...

    前言

    参考翻译自Dan Sunday的文章The Convex hull of a point set

    凸包计算是计算集合中的一个经典问题。这一问题有许多变种,其中最普遍的形式是计算平面离散点集的最小凸集(称为“凸包”)。这一算法也可以用于多边形或者一组线段集的凸包求解。在许多领域中都需要用到凸包算法,例如:碰撞检测、消隐、形状分析等。

    最流行的凸包算法是Graham扫描(Graham scan)算法和Preparata & Hong提出的“分而治之”(Divide-and-Conquer)算法。这两者的时间复杂度都是O(nlogn),但是Graham算法的常数因子更小,实际运行要快得多。不过“分而治之”算法的优点在于可以方便自然地扩展到3D,乃至更多维度,而Graham算法则不能。

    下面是一些著名的2D凸包算法的对比情况(其中,n是点集中点的个数,h是输出凸包的点数):

    算法时间复杂度发明者
    礼物包裹算法(Gift Wrapping)O(nh)Chand & Kapur, 1970
    Graham Scan算法O(nlogn)Graham, 1972
    Jarvis March算法O(nh)Jarvis, 1973
    QuickHullO(nh)Eddy, 1977
    Divide-and-ConquerO(nlogn)Preparata & Hong, 1977
    单调链算法Monotone ChainO(nlogn)Andrew, 1979
    增量算法O(nlogn)Kallay, 1984
    Marriage-before-ConquestO(nlogh)Kirkpatrick & Seidel, 1986

    凸包的定义

    对于一个几何对象(一个点集或者多边形),我们将包含所有对象的最小凸集称为凸包。但是这种定义太抽象,对于平面点集的凸包,我们可以给出下面几种等价定义:
    定义1:对于S内部任意两点P和Q,如果线段PQ完全在S内部,那么S是凸包(凸多边形)。
    Def 1

    定义2:S是凸包,如果它等价于包含它的所有半平面的交集。
    这个定义可以这样理解:逆时针沿着S的边界走一圈,在每条边左手侧的半平面称为“左半平面”,所有边的左半平面求交集,就得到了一个多边形区域V,如果V = S,那么S必是凸多边形。

    定义3: 点集{P}的凸包是所有包含点集{P}的凸多边形中的面积最小者。

    2D凸包算法详解

    下面详细说明两个简单高效的凸包算法:Graham scan算法和Monotone Chain算法。两者都用到了栈操作(stack)。在实践中,两者都非常高效,Monotone Chain算法略快,因为它事先进行了排序且它的拒绝测试比较高效。

    Graham Scan算法

    在许多计算几何的书籍中,都将Graham Scan算法作为第一个计算几何经典算法进行讲解。因为随着问题规模的增长,该算法达到最优的渐近时间效率(O(nlogn))。该算法首次由O’Rourke在1998给出详细的实现Computational Geometry in C。O(nlogn)的时间上界主要由初始化时,进行的角度排序决定;后续该算法基于栈操作,进行了最多2n次出栈和入栈,时间复杂度是O(n)。因此,算法运行效率很高。

    记S={P}是输入的点集。

    1. 从S中挑出一个可以确定是凸包中的点,这很容易做到,只需选取最下最右点(y最小,x最大)即可,时间复杂度是O(n),将该点记为P0;
    2. 将其他所有点以P0P与x轴的夹角(逆时针方向)为基准进行排序(从小到大),如果两个点有相同的夹角则删除离P0较近的点;为了高效起见,进行排序比较时,不必计算P1、P2的角度值。事实上,角度的计算需要不精确且比较慢的三角函数计算,会引入数值误差。如下图,我们只需观察P2是否在P0P1的左侧,即可判断P2P0的角度与P1P0的关系。这个计算仅需要一次叉乘操作(参考下面的isLeft函数),非常快速。
      isLeft
      排序之后, 点集S={P0,P1,P2…Pn-1}如下图所示:
      sorted
      3. 按次序遍历S中的每个点,检查其是否属于凸包顶点。检查算法是一个利用栈操作的归纳增量过程。

    下面对第3步进行详细讲解:

    首先将P0入栈,始终确保栈中的点构成凸包。P1直接入栈。

    在第k步时,计算当Pk加入后,当前凸包是否改变。由于S是排过序的,因此Pk一定是当前凸包(P0~Pk-1的凸包)之外的一个点,因此Pk作为一个新的顶点加入到栈中。

    但是Pk的加入可能使得之前栈中的顶点不再是凸包顶点,就需要将非凸包顶点从栈中弹出。通过检查Pk与栈顶两个点组成的直线的关系(是否在左侧,使用isLeft函数)就可做到。有两种情况:

    • 如果Pk是在左侧,那么先前的凸包是准确的,Pk直接加入到栈中。
    • 如果Pk在栈顶两个点组成的直线的右侧,那么将栈顶元素弹出,再次进行上述检查,直到Pk在栈顶两点组成直线的左侧。最后将Pk入栈。
      下图清晰地说明了第二种情况:
      testPk

    接下来,算法处理下一个点Pk+1.

    当k = n-1,所有点处理完,保留在栈中的点就是最后的结果。对于S中的每个点,上述算法最多有一次入栈和一次出栈操作,因此总共有最多2n次栈操作。

    算法伪代码归纳如下:

    Graham Scan算法伪代码如下:

    Input: a set of points S = {P = (P.x,P.y)}
    在S中选择最下最右点P0,作为起始点;
    以P0为基准点,将S中的点沿着逆时针方向排序 {
          使用 isLeft() 做比较函数,
          抛弃同一角度上的距离P0较近的点
    }
    记 P[N] 为排序后的点集,其中 P[0]=P0.

    Push P[0] and P[1] onto a stack Φ \Phi Φ
    while i < N
    {
          Let PT1 = the top point on OMEGA
          If (PT1 == P[0]) {
                Push P[i] onto Φ \Phi Φ
                i++ // increment i
          }
          Let PT2 = the second top point on Φ \Phi Φ
          If (P[i] is strictly left of the line PT2 to PT1) {
                Push P[i] onto Φ \Phi Φ
                i++ // increment i
          }
          else
                Pop the top point PT1 off the stack
    }
    Output: Φ \Phi Φ = the convex hull of S.

    **************************************************************
    // isLeft(): tests if a point is Left|On|Right of an infinite line.
    //    Input:  three points P0, P1, and P2
    //    Return: >0 for P2 left of the line through P0 and P1
    //            =0 for P2 on the line
    //            <0 for P2 right of the line
    isLeft( Point P0, Point P1, Point P2 )
    {
        return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
    }
    
    

    Monotone Chain算法

    Andrew在1979年,提出了Graham scan的一个改进版本,直接通过点的x、y坐标值进行线性的排序。由于其比较函数更加简单,因此比Graham scan中的周向排序速度更快。Monotone Chain算法将点集分为上下两个单调链,分别计算其凸包,算法由此得名。与Graham scan一样,其时间复杂度受限于排序算法,也是O(nlogn)。算法的主要步骤如下:

    首先,将S={P0,P1,P2…Pn-1}进行按照先x后y的规则(从小到大)进行排序;将x的最小最大值分别记为xmin、xmax。
    记点Pminmin为x=xmin且y为x坐标相同的点中的最小值,Pminmax为x=xmin且y为x坐标相同的点中的最大值。当只有一个点的x坐标=xmin时,Pminmax=Pminmin。
    同样地,我们定义Pmaxmin为x=xmax且y为x坐标相同的点中的最小值,Pmaxmax同理。

    连接Pminmin与Pmaxmin,我们得到Lmin;连接Pminmax与Pmaxmax,得到Lmax,如下图所示:
    chain
    接下来,算法构造一个下部的凸包(Lmin之下) Φ m i n \Phi_{min} Φmin,以及一个上部的凸包 Φ m a x \Phi_{max} Φmax(Lmax之上),然后将两者合并起来就得到了最终的完整凸包 Φ \Phi Φ.

    下部和上部的凸包都使用类似与Graham scan中的栈方法来构造。

    以下部为例,首先将Pminmin入栈,然后依次处理minmin到maxmin之间的点。对于 Φ m i n \Phi_{min} Φmin只需要考虑在Lmin之下的点。假设在第k步,Pk在Lmin之下,如果栈中只有一个点Pminmin,那么直接将Pk入栈。否则,需要判断Pk是否改变了当前的凸包。判断方法与Graham扫描类似,检查Pk是否在栈顶两个元素组成的向量的左侧。如果是,直接入栈;如果不是,弹出栈顶元素,继续检查直到Pk入栈。所有点都处理后,将Pmaxmin入栈即可,于是就得到了完整的 Φ m i n \Phi_{min} Φmin

    对于上半部分,以相同的方式处理。不过,需要从Pmaxmax开始,按照降序依次处理S中的点,并且只考虑在Lmax之上的点。当 Φ m i n \Phi_{min} Φmin Φ m a x \Phi_{max} Φmax都计算出来后,很容易将两者合并起来(不过需要注意端点的重复)。

    Andrew’s Monotone Chain的伪代码如下:

        Input: a set S = {P = (P.x,P.y)} of N points 
        按照先x后y,从小到大对点集进行排序, 
        记P[]为N个点排序之后的数组。
        计算4个一定在凸包上的极值点Pminmin、Pminmax、Pmaxmin、Pmaxmax。
        按以下步骤计算下部的凸包:
        (1)Lmin为P[minmin]-P[maxmin]的连线
        (2)P[minmin]入栈
        (3)对于下标在minmax到maxmin之间的点P[i](minmax+1 <= i <=maxmin-1):
                 if   P[i]在Lmin下方  then
                    while (至少有两个点在栈中) do
                    {
                        PT1为栈顶元素,PT2为次顶元素;
                        if  P[i]在向量PT2-PT1的左侧 then
                           Break;
                        弹出 PT1; 
                    }
                    P[i]入栈。
             
         (4)P[maxmin]入栈。
         同样地,计算上半部的凸包。
         合并上下半部的结果,得到最终的凸包。
    

    Andraw‘s Monotone Chain算法的C++实现

    下面是单调链算法的一个C++实现。isLeft函数,在上一节中已经给出,这里不再重复。Point是平面二维点的定义{float x, y;}。

    // chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
    //     Input:  P[] = an array of 2D points
    //                  presorted by increasing x and y-coordinates
    //             n =  the number of points in P[]
    //     Output: H[] = an array of the convex hull vertices (max is n)
    //     Return: the number of points in H[]
    int chainHull_2D( Point* P, int n, Point* H )
    {
        // the output array H[] will be used as the stack
        int    bot=0, top=(-1);   // indices for bottom and top of the stack
        int    i;                 // array scan index
    
        // Get the indices of points with min x-coord and min|max y-coord
        int minmin = 0, minmax;
        float xmin = P[0].x;
        for (i=1; i<n; i++)
            if (P[i].x != xmin) break;
        minmax = i-1;
        if (minmax == n-1) {       // degenerate case: all x-coords == xmin
            H[++top] = P[minmin];
            if (P[minmax].y != P[minmin].y) // a  nontrivial segment
                H[++top] =  P[minmax];
            H[++top] = P[minmin];            // add polygon endpoint
            return top+1;
        }
    
        // Get the indices of points with max x-coord and min|max y-coord
        int maxmin, maxmax = n-1;
        float xmax = P[n-1].x;
        for (i=n-2; i>=0; i--)
            if (P[i].x != xmax) break;
        maxmin = i+1;
    
        // Compute the lower hull on the stack H
        H[++top] = P[minmin];      // push  minmin point onto stack
        i = minmax;
        while (++i <= maxmin)
        {
            // the lower line joins P[minmin]  with P[maxmin]
            if (isLeft( P[minmin], P[maxmin], P[i])  >= 0 && i < maxmin)
                continue;           // ignore P[i] above or on the lower line
    
            while (top > 0)         // there are at least 2 points on the stack
            {
                // test if  P[i] is left of the line at the stack top
                if (isLeft(  H[top-1], H[top], P[i]) > 0)
                     break;         // P[i] is a new hull  vertex
                else
                     top--;         // pop top point off  stack
            }
            H[++top] = P[i];        // push P[i] onto stack
        }
    
        // Next, compute the upper hull on the stack H above  the bottom hull
        if (maxmax != maxmin)      // if  distinct xmax points
             H[++top] = P[maxmax];  // push maxmax point onto stack
        bot = top;                  // the bottom point of the upper hull stack
        i = maxmin;
        while (--i >= minmax)
        {
            // the upper line joins P[maxmax]  with P[minmax]
            if (isLeft( P[maxmax], P[minmax], P[i])  >= 0 && i > minmax)
                continue;           // ignore P[i] below or on the upper line
    
            while (top > bot)     // at least 2 points on the upper stack
            {
                // test if  P[i] is left of the line at the stack top
                if (isLeft(  H[top-1], H[top], P[i]) > 0)
                     break;         // P[i] is a new hull  vertex
                else
                     top--;         // pop
            }
            H[++top] = P[i];        // push P[i] onto stack
        }
        if (minmax != minmin)
            H[++top] = P[minmin];  // push  joining endpoint onto stack
    
        return top+1;
    }
     
    
    展开全文
  • 平面点集的最小包围圆 hdu 3932

    千次阅读 2015-01-22 19:33:12
    平面点集的最小包围圆 1、 问题背景   考察固定在工作平台上的一直机械手,要捡起散落在不同位置的多个零件,并送到别的地方。那么,这只机械手的底座应该选在哪里呢?根据直觉,应该选在机械手需够着的那些位置的...
  • Opencv源码之平面点集的最小包围圆

    千次阅读 2016-05-28 12:32:48
    平面点集的最小包围圆 --Cracent整理 2016.5.28 目录 1、问题背景.... 1 2、算法及原理.... 1 3、算法(摘自OPENCV)... 1 4、基础数学知识.... 7 三角形的外心.... 7 三角形的三条垂直平分线必交于...
  • 第四课 二维点集的凸包 特征描述: 应用: 类似于游戏的命中判定、碰撞箱。 题目的标准定义 如何提高效率: 1、已经判断出不可能是极点的的应不再参与循环。 2、序号123的、231的、312的都会作为三角形...
  • 定义 P 中某x,如果x满足 P 中任意点都不在 x 的右上方区域(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”的集合。(所有的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) ) &embp;如...
  • 定义 P 中某x,如果x满足 P 中任意点都不在 x 的右上方区域(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”的集合。(所有的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) ) 如下图:实心...
  • 【题意】给出平面的一系列的的坐标,求其中组成凸边形的个数
  • 点集

    千次阅读 2019-03-15 17:18:57
    点集:习惯上把集合中元素有某种关系、集合有某种结构的集合,叫做空间或者点集。 【这里的“关系”和集论初步的“关系”一致,而“结构”一般比较抽象,例如“度量”,“距离”就属于集合的一种结构。】 ...
  • 对所有的坐标对y进行排序,然后按y排序顺序由大到小进行遍历,定义一个变量来保存当前已经遍历坐标的最大x值,如果,后序节点的x值大于保存的最大x值,则输出。因为已经对y按大小进行排序,越往后遍历y值越小,...
  • 设$E$是复平面$\mathbb C$中的任意点集,那么$\mathbb C$中的点可分为三类: 1)点$a\in\mathbb C$称为$E$的内点,如果存在$r>0$使得$B(a,r)\subset E$,内点的全体称为内部,用集合用$E^{\circ}$表示; 2)称为$E$的...
  • 求半平面交的点集

    千次阅读 2015-08-19 16:12:11
    //////////////////////////////////////////////////////////////////////////////////////////////...struct Line { //定义直线类型 Point P; // 直线上任意一点 Vector v; // 方向向量 double ang; // 极角,即从
  • 定义平面点集可以表示为由区间 到复平面的连续映射下的像集,则该点集就称为一条平面曲线,该映射称为该曲线的一个参数方程.按照参数的增大或减小的方向可给出曲线的方向,参数增大的方向为曲线的正方向(默认)...
  • 定义 P 中某x,如果x满足 P 中任意点都不在 x 的右上方区域(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”的集合。(所有的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) ) 如下图:...
  • 定义 P 中某x,如果x满足 P 中任意点都不在 x 的右上方区域(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”的集合。(所有的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) ) 如下图:...
  • Point类是平面二维类:私有属性普通字段x,y(表示每个的坐标);方法有四个:init__初始化方法(用来初始化 坐标x,y),获取私有普通字段x,y的方法Get X与Get Y(用来获取私有普通字段x,y的值)、专有方法__...
  • 三维点集拟合:平面拟合、RANSAC、ICP算法 一、拟合一个平面 空间平面方程的一般表达式为:Ax+By+Cz+D=0; 则有:平面法向量为n=(A,B,C). 第一种方法:对于空间中n个(n3) 空间中的离散得到拟合平面,...
  • 在这里描述定义两个完整类Point 与 类Circle: class Point: class Circle:
  • 最小点集覆盖圆

    千次阅读 2011-10-04 11:08:16
    转载自:http://soft.cs.tsinghua.edu.cn/blog/?q=node/1066 平面点集的最小包围圆 平面点集的最小包围圆 1、 问题背景   考察固定在工作平台上的一直机械手,要捡起散落在不同位置的多个
  • 从二维点集重建平面形状-浅议凹包算法 - Jumanco&Hide 主题 算法 向量 游戏开发 问题背景 近期遇到一个计算几何问题,需要从集中重建一个合理的几何形状。这个问题既有二维的也有三维的,二...
  • 定义 P 中某x,如果x满足 P 中任意点都不在 x 的右上方区域(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”的集合。(所有的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) ) 如下图:...
  • ------------------------------------ ...-- Return : int 0 -- 不存在于搜索串的范围 -- 1 -- 存在于 -- 转载请注明出处。更多请访问: http://blog.csdn.net/happyflystone -- 原帖地址: ...
  • 多维点集问题的分治技术1....多维点集问题在计算机科学领域有着十分重要的地位,其旨在解决高维空间最大的搜索、ECDF函数的计算以及最近的搜索等等,这些问题在实际应用中有着非常重要的作用,比如在数据库中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,028
精华内容 2,011
关键字:

平面点集内点的定义