二维计算几何基础
我们将需要解决的几何问题的范围限制在二维平面内,这样就用到了二维计算几何。
要用电脑解平面几何题?数学好的同学们笑了。
某同学:来来来我给你们表演
分钟算解析几何大题。 5
我们并不是用计算机算数学卷子上的几何题去了,而是解决一些更加复杂的几何相关问题。
为了解决复杂且抽象的问题,我们一定要选择合适的研究方法。对于计算机来说,给它看几何图形……
我们可以把要研究的图形放在平面直角坐标系或极坐标系下,这样解决问题就会方便很多。
前置技能¶
如并不了解:
- 几何基础
- 平面直角坐标系
- 向量(包括向量积)
- 极坐标与极坐标系
请先阅读OI Wiki - 数学 - 杂项。
图形的记录¶
点¶
在平面直角坐标系下,点用坐标表示,比如点
我们记录其横纵坐标值即可。用 pair
或开结构体记录均可。
在极坐标系下,用极坐标表示即可。记录其极径与极角。
向量¶
由于向量的坐标表示与点相同,所以只需要像点一样存向量即可(当然点不是向量)。
在极坐标系下,与点同理。
线¶
直线与射线¶
一般在解数学题时,我们用解析式表示一条直线。有一般式
这些式子最后都逃不过最后的结果——代入解方程求值。
解方程什么的最讨厌了,有什么好一点的方法吗?
考虑我们只想知道这条直线在哪,它的倾斜程度怎么样。于是用直线上的一个点先大致确定位置,用一个向量表示它的倾斜程度,好了,这条直线确定了。
因此我们记录的是:直线上一点和直线的方向向量。
线段¶
线段很好记录:只需要记录左右端点即可。
在极坐标系下,记录线是比较麻烦的,因此大多数直线问题都在平面直角坐标系下解决。
多边形¶
开数组按一定顺序记录多边形的每个顶点即可。
特殊地,如果矩形的各边均与某坐标轴平行的话,我们只记录左下角和右上角的顶点即可。
曲线¶
一些特殊曲线,如函数图像等一般记录其解析式。对于圆,直接记录其圆心和半径即可。
基本公式¶
正弦定理¶
在三角形
其中,
余弦定理¶
在三角形
上述公式的证明略。均为人教版高中数学 A 版必修五内容。
基本操作¶
判断一个点在直线的哪边¶
某同学:我能看出来!
我们有直线上的一点
我们利用向量积的性质,算出
可以画一下图,用右手定则感受一下。
快速排斥实验与跨立实验¶
某同学:捂我嘴干什么我可以……%¥*……%
我们现在想判断两条线段是否相交。
首先特判一些特殊情况。如果两线段平行,自然不能相交。这种情况通过判断线段所在直线的斜率是否相等即可。
当然,如果两线段重合或部分重合,只需要判断是否有三点共线的情况即可。
如果两线段的交点为其中一条线段的端点,仍然判断是否有三点共线的情况即可。
还有些显然不相交的情况,我们口头上称之为「两条线段离着太远了」。可什么是「离着远」,怎么判断它呢?
规定「一条线段的区域」为以这条线段为对角线的,各边均与某一坐标轴平行的矩形所占的区域,那么可以发现,如果两条线段没有公共区域,则这两条线段一定不相交。
比如有以下两条线段:
它们占用的区域是这样的:
于是可以快速地判断出来这两条线段不相交。
这就是 快速排斥实验 。上述情况称作 未通过快速排斥实验 。
未通过快速排斥实验是两线段无交点的 充分不必要条件 ,我们还需要进一步判断。
因为两线段
这就是 跨立实验 ,如果对于两线段
通过跨立实验是两线段相交的 充要条件 ,因此直接利用跨立实验判断即可。
由于快速排斥实验只需要少量比较而不需要计算,所以先用快速排斥实验判断可以减小一部分常数。
判断一点是否在任意多边形内部¶
某同学:我还能 %$#^%*(%&))
在计算几何中,这个问题被称为PIP 问题,已经有一些成熟的解决方法,下面依次介绍。
光线投射算法_(Ray casting algorithm)_¶
在这里可以看到最原始的思路。
我们先特判一些特殊情况,比如「这个点离多边形太远了」。考虑一个能够完全覆盖该多边形的最小矩形,如果这个点不在这个矩形范围内,那么这个点一定不在多边形内。这样的矩形很好求,只需要知道多边形横坐标与纵坐标的最小值和最大值,坐标两两组合成四个点,就是这个矩形的四个顶点了。
还有点在多边形的某一边或某顶点上,这种情况十分容易判断(留作课后作业)。
我们考虑以该点为端点引出一条射线,如果这条射线与多边形有奇数个交点,则该点在多边形内部,否则该点在多边形外部,我们简记为 奇内偶外 。这个算法同样被称为奇偶规则_(Even-odd rule)_。
由于Jordan curve theorem,我们知道,这条射线每次与多边形的一条边相交,就切换一次与多边形的内外关系,所以统计交点数的奇偶即可。
这样的射线怎么取?可以随机取这条射线所在直线的斜率,建议为无理数以避免出现射线与多边形某边重合的情况。
在原版代码中,使用的是记录多边形的数组中最后一个点作为射线上一点,这样统计时,如果出现射线过多边形某边或某顶点时,可以规定射线经过的点同在射线一侧,进而做跨立实验即可。
回转数算法_(Winding number algorithm)_¶
回转数是数学上的概念,是平面内闭合曲线逆时针绕过该点的总次数。很容易发现,当回转数等于
如何计算呢?我们把该点与多边形的所有顶点连接起来,计算相邻两边夹角的和。注意这里的夹角是 有方向的 。如果夹角和为
求两条直线的交点¶
某同学:这还不简单联立方程 #%$&%)(Y(*&UIG))
首先,我们需要确定两条直线相交,只需判断一下两条直线的方向向量是否平行即可。如果方向向量平行,则两条直线平行,交点个数为
那么,问题简化为我们有直线
如果两直线相交,则交点只有一个,我们记录了直线上的一个点和直线的方向向量,所以我们只需要知道这个点与交点的距离
考虑构造三角形,利用正弦定理求解
由上图可知,
作商得:
可以看出,
同时,我们将
于是,只需要将点
求任意多边形的周长和面积¶
求任意多边形的周长¶
直接计算即可,简洁即美德。
求任意多边形的面积¶
考虑向量积的模的几何意义,我们可以利用向量积完成。
将多边形上的点逆时针标记为
圆与直线相关¶
求直线与圆的交点¶
首先判断直线与圆的位置关系。如果直线与圆相离则无交点,若相切则可以利用切线求出切点与半径所在直线,之后转化为求两直线交点。
若有两交点,则可以利用勾股定理求出两交点的中点,然后沿直线方向加上半弦长即可。
求两圆交点¶
首先我们判断一下两个圆的位置关系,如果外离或内含则无交点,如果相切,可以算出两圆心连线的方向向量,然后利用两圆半径计算出平移距离,最后将圆心沿这个方向向量进行平移即可。
如果两圆相交,则必有两个交点,并且关于两圆心连线对称。因此下面只说明一个交点的求法,另一个交点可以用类似方法求出。
我们先将一圆圆心与交点相连,求出两圆心连线与该连线所成角。这样,将两圆心连线的方向向量旋转这个角度,就是圆心与交点相连形成的半径的方向向量了。
最后还是老套路——沿方向向量方向将圆心平移半径长度。
极角序¶
!!! 例题「JOI Spring Camp 2014 Day4」两个人的星座
平面内有
如果两个三角形不相交,则一定可以做出两条内公切线,如果相交或内含是做不出内公切线的。三角形的公切线可以类比圆的公切线。
先枚举一个原点,记为
然后根据点枚举公切线,记枚举到的点为
统计完后转公切线,那么点
这样,可以发现,同一对三角形最终被统计了
分析一下算法复杂度,我们枚举了一个原点,然后对于每一个原点将剩余点排序后线性统计。于是时间复杂度为
代码编写注意事项¶
由于计算几何经常进行 double
类型的浮点数计算,因此带来了精度问题和时间问题。
有些问题,例如求点坐标均为整数的三角形面积,可以利用其特殊性进行纯整数计算,避免用浮点数影响精度。
由于浮点数计算比整数计算慢,所以需要注意程序的常数因子给时间带来的影响。
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用