计算几何相关

##向量表示法 这里最适合的就是用一个二维点对$(a,b)$来表示了。 ##点积 ${a.xb.x+a.yb.y}$ 在向量的含义下:$\vec{a}·\vec{b}=|\vec{a}||\vec{b}|cos$ ##叉积 ${a.xb.y-a.yb.x}$ 这个东西很有用,首先这个东西的绝对值就是两个向量构成的三角形的面积的二倍。 证明的话只需要把图画出来,然后过向量端点的四条平行于坐标轴的直线将包含了整个向量的矩形分割成四个部分,然后割补法算一算就好了。 发现上面写面积的时候带了绝对值,当$b$在$a$的逆时针方向时,即$b$在$a$的左侧时,两者的叉积为正,否则叉积为负,所以通过叉积也可以很方便的计算向量之间的位置关系。 ##转角 把向量转动一个角度$\alpha$。 显然向量如果把它的模长变为$1$,就可以在单位圆上对应一个点,假设其模长为$R$,那么一个向量可以写为$(R\cos\beta,R\sin\beta)$,那么转角只需要改变$\beta$即可。 再用三角函数的和角公式可以很容易拆分。 ##极角 其实就是如何计算上面那个$\beta$,直接$atan2(a.y,a.x)$就好了。 ##两条直线的交点 接下来所有的直线基本都用线段代替。 而线段基本都用一个点+一个方向向量表示。 假设$a1,b1$是点,$a2,b2$是方向向量。 $b2\times (b1-a1))$与$b2\times a2$的比值就只与交点到直线$b$与$a1+a2$到直线$b$的距离相关。 那么通过这个比例可以很容易算出交点的确切位置。 ##线段之间是否有交 似乎可以算下直线交点,判断是否在线段上。 假设$a_1,a_2,b_1,b_2$是两条线段的端点。 如果$(b_1-a_1)\times (b1-a2)$与$(b_2-a_1)\times (b_2-a_2)$异号, 并且$(a_1-b_1)\times (a_1-b_2)$与$(a_2-b_1)\times (a_2-b_2)$异号, 那么两条线段有交。 ##上面的一部分代码

struct Point{double x,y,ang;};

bool operator

Point operator+(Point a,Point b){return (Point){a.x+b.x,a.y+b.y};}

Point operator-(Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}

Point operator*(Point a,double b){return (Point){a.x*b,a.y*b};}

Point operator/(Point a,double b){return (Point){a.x/b,a.y/b};}

double operator*(Point a,Point b){return a.x*b.x+a.y*b.y;}

double Cross(Point a,Point b){return a.x*b.y-a.y*b.x;}

double Len(Point a){return sqrt(a.x*a.x+a.y*a.y);}

double Dis(Point a,Point b){return Len(a-b);}

Point Rotate(Point p,double a){double c=cos(a),s=sin(a);return (Point){p.x*c-p.y*s,p.x*s+p.y*c};}

struct Line{Point a,v;};

Point Intersection(Line a,Line b)

{

Point c=b.a-a.a;

double t=Cross(b.v,c)/Cross(b.v,a.v);

return a.a+a.v*t;

}

##Graham扫描法 找到最左下角的点,其他点以这个点为原点进行极角排序。 按照次序依次考虑每个点加进凸包后是否能够删去之前的一些点。 通过叉积可以判断当前凸包中的是否有可以被删去的点。 时间复杂度$O(nlogn)$,瓶颈在于极角排序。 叉积判断那里判断方法很多,并不唯一。 求凸包前可以给坐标加上$\mbox{eps}$,防止一些毒瘤出题人造了一堆奇怪的数据导致凸包跑出来有问题。(比如说共线之类的)

Point S[MAX];int top;

void Graham(Point *p,int n)

{

int pos=1;

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

if(p[i].x

pos=i;

swap(p[1],p[pos]);

for(int i=2;i<=n;++i)p[i].ang=atan2(p[i].y-p[1].y,p[i].x-p[1].x);

sort(&p[2],&p[n+1]);S[++top]=p[1];S[++top]=p[2];

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

{

while(top>2&&Cross(p[i]-S[top],p[i]-S[top-1])>=0)--top;

S[++top]=p[i];

}

}

##凸包面积 算出凸包之后选定一条边,将凸包分割为若干三角形,叉积计算面积。 注意一下叉积的正负性,不要乱来。

##判断点是否在多边形内 向一侧做一条射线,计算与多边形边界的交的次数,如果为奇数则在内部,否则在外部。 注意射线过顶点的情况,最好给斜率加上一定扰动,防止这类情况的出现。 ##判断点是否在凸包内 一种傻逼做法就是算这个点和凸包的每条边形成的三角形面积和,看看是否等于凸包面积。 机智点的话,找到凸包上极角在这个点两侧的两个点,叉积随便判断一下就好了。 ##旋转卡壳(怎么读我不负责) 比如说用来求解凸包的直径,即凸包上最远的两点之间的距离。 通过凸包的单调性来实现。 比如求解凸包的直径:

double Diameter(Point *S,int n)

{

double d=0;S[n+1]=S[1];if(n==2)return Dis(S[1],S[2]);

for(int i=1,j=3;i<=n;++i)

{

while(Cross(S[j]-S[i],S[j]-S[i+1])

j=(j==n)?1:j+1;

d=max(d,max(Dis(S[j],S[i]),Dis(S[j],S[i+1])));

}

return d;

}

所有要求的东西都会随着你的逆时针旋转而旋转,所以直接利用单调性暴力向前即可。 这样子的复杂度是线性的。

再比如说求解最小矩形覆盖

void ScanLine(int n)

{

S[n+1]=S[1];S[0]=S[n];

for(int i=1,j1=3,j2=3,j3=n;i<=n;++i)

{

if(i==1)while((S[i]-S[i+1])*(S[j3-1]-S[j3])>0)j3=(j3==1)?n:j3-1;

while(Cross(S[j1]-S[i],S[j1]-S[i+1])<=Cross(S[j1+1]-S[i],S[j1+1]-S[i+1]))j1=(j1==n)?1:j1+1;

while((S[i+1]-S[i])*(S[j2+1]-S[j2])>0)j2=(j2==n)?1:j2+1;

while((S[i+1]-S[i])*(S[j3+1]-S[j3])<0)j3=(j3==n)?1:j3+1;

Line l0=(Line){S[i],S[i+1]-S[i]};

Line l1=(Line){S[j1],S[i]-S[i+1]};

Line l2=(Line){S[j2],Rotate(S[i+1]-S[i],Pi/2)};

Line l3=(Line){S[j3],Rotate(S[i]-S[i+1],Pi/2)};

tmp[1]=Intersection(l0,l2);tmp[2]=Intersection(l2,l1);

tmp[3]=Intersection(l1,l3);tmp[4]=Intersection(l3,l0);

double area=Dis(tmp[1],tmp[2])*Dis(tmp[2],tmp[3]);

if(area

ans=area,Ans[1]=tmp[1],Ans[2]=tmp[2],Ans[3]=tmp[3],Ans[4]=tmp[4];

}

}

注意到$i=1$的时候先顺时针找到了正确的位置,接下来才能逆时针向前继续寻找。

##最小圆覆盖 一个暴力的做法。 枚举第一个点,考虑当前圆是否包含了这个点,如果没有,则把圆变成以这个点为圆心,半径为$0$的圆。再枚举第二个点,考虑圆是否包含了这个点,如果没有,则把圆变成以这两个点的中点为圆心,半径为两点距离一半的圆。再枚举第三个点,节点是否在圆内,如果不在,则把圆直接变成这三个点的外接圆。 看起来是三方的,然而随机打乱点之后期望$O(n)$ 代码戳这里

##半平面交 看起来似乎是有两种方法的。 第一种是在线的增量法。 先存下当前的半平面,初始时我们认为是一个极大的凸多边形。 每次新加入一个向量(代表一条直线),让其和前面的所有边求交,只有这向量左侧的才是合法的东西,然后把新的交点给加入进来。时间复杂度是$O(n^2)$的。这种方法支持随时增量,可以查询中间任意时刻的半平面交。 代码如下,注意是否有交时的正负号,以及是否有等于的情况。

void pre()

{

top=0;

S[++top]=(Point){inf,inf};

S[++top]=(Point){-inf,inf};

S[++top]=(Point){-inf,-inf};

S[++top]=(Point){inf,-inf};

}

void Cut(Line a)

{

S[top+1]=S[1];int tot=0;

for(int i=1;i<=top;++i)

{

double v1=Cross(a.v,S[i]-a.a);

double v2=Cross(a.v,S[i+1]-a.a);

if(v1>=0)tmp[++tot]=S[i];

if(v1*v2<0)tmp[++tot]=Intersection(a,(Line){S[i],S[i+1]-S[i]});

}

top=tot;for(int i=1;i<=top;++i)S[i]=tmp[i];

}

还有一种方法是$O(nlogn)$的,然而我不太会,所以先咕了。

##自适应辛普森积分 求积分。 把函数$F(x)$看成二次函数去近似比,每次二分当前位置,如果左侧估计出来的值加上右侧估计出来值与整个区间估计出来的值之间的差小于$eps$,那么就当做这个区间的值,否则则递归处理。 代码如下:

double Simpson(double l,double r){return (r-l)*(F(l)+F(r)+4*F((l+r)/2))/6;}

double Simpson(double l,double r,double ans)

{

double mid=(l+r)/2,L=Simpson(l,mid),R=Simpson(mid,r);

if(fabs(L+R-ans)

return Simpson(l,mid,L)+Simpson(mid,r,R);

}

##闵可夫斯基和 给定两个点集$A,B$,显然这些点都可以用向量表示,它们的闵可夫斯基和$C={\vec{a}+\vec{b}|\vec{a}\in A,\vec{b}\in B}$。 现在给定了两个凸包,求他们的闵可夫斯基和。 画图之后不难发现他们的闵可夫斯基和形成的凸包其实就是这两个凸包的所有边按照极角排序的顺序重新拼接在一起的。所以求出两个凸包后直接按照逆时针顺序归并排序就可以得到闵可夫斯基和。 更加详细的内容参考$\mbox{lalaxu}$博客 代码:

void Minkowski(Point *p1,Point *p2,int n,int m)

{

p1[n+1]=p1[1];p2[m+1]=p2[1];

for(int i=1;i<=n;++i)t1[i]=p1[i+1]-p1[i];

for(int i=1;i<=m;++i)t2[i]=p2[i+1]-p2[i];

S[top=1]=p1[1]+p2[1];

int i=1,j=1;

while(i<=n&&j<=m)

{

++top;S[top]=S[top-1];

if(Cross(t1[i],t2[j])>=0)S[top]=S[top]+t1[i++];

else S[top]=S[top]+t2[j++];

}

while(i<=n)++top,S[top]=S[top-1]+t1[i++];

while(j<=m)++top,S[top]=S[top-1]+t2[j++];

}

注意一下我这份代码求出来之后要把最后一个点给丢掉,要不然左下角的那个点会被计算两次。

##其他的 似乎没有啥东西了? 三维计算几何就算了,懒得搞了。

python 计算面积 比计算几何慢_计算几何相关 - osc_kbyywydz的个人空间 - OSCHINA - 中文开源技术交流社区...相关推荐

  1. python莫比乌斯_莫比乌斯函数 - osc_7eqzxl4g的个人空间 - OSCHINA - 中文开源技术交流社区...

    前导 要学习莫比乌斯函数 需要学习 到 积性函数,深度理解欧拉筛. 先说说什么是积性函数吧. 积性函数 其实积性函数非常好理解, 定义 积性函数:若gcd(a,b)=1,且满足f(ab)=f(a)f( ...

  2. python蓝牙上位机开发_python做上位机 - osc_2frv0wjp的个人空间 - OSCHINA - 中文开源技术交流社区...

    参考文章: https://blog.csdn.net/dgut_guangdian/article/details/78391270 https://www.cnblogs.com/lanceyu/ ...

  3. java中打印俄文字母表_符号大全 - simonkjer的个人空间 - OSCHINA - 中文开源技术交流社区...

    符号大全 ︻︼︽︾〒↑↓☉⊙●〇◎¤★☆■▓「」『』◆◇▲△▼▽◣◥◢◣◤ ◥№↑↓→←↘↙Ψ※㊣∑⌒∩[][]@ξζω□∮〓※>∏卐√ ╳々♀♂∞①ㄨ≡╬╭╮╰╯╱╲ ▂ ▂ ▃ ▄ ▅ ▆ ...

  4. java中PL层_安装pljava - RuralHunter的个人空间 - OSCHINA - 中文开源技术交流社区

    pljava是pgsql跟java的桥接,安装以后就可以在pgsql里面调用java了.这里记录一下我在ubuntu server下安装的过程 1. 下载源码编译 很简单,下载,解压,设置一下JAVA ...

  5. java 格式化手机号码_格式化手机号码 - T_SevenS的个人空间 - OSCHINA - 中文开源技术交流社区...

    手机号会存在加区号/国际ip号的问题,如+8615851732142.008615851732142.17951158517332142或者0158517321142,上面几个号码实际都是同一个号码, ...

  6. php 计算当前网速,php 测试网速 - qiuzhizhe的个人空间 - OSCHINA - 中文开源技术交流社区...

    // $res = file_get_contents('1.log'); // class b{ //     function json_decode(){ //         echo '13 ...

  7. python计算等额本金_等额本金-逆推 - tedzheng的个人空间 - OSCHINA - 中文开源技术交流社区...

    等额本金是递归逆推的一个方法: 父亲准备4年的生活费,使且整存零取的方式,控制每月底只能提取1000元,假设银行年利息为1.71% 思路: 第48个月取出1000,则要先求出47个月时银行存款的具体金 ...

  8. 人工鱼群算法python代码_人工鱼群算法python_鱼群算法 - Brillou的个人空间 - OSCHINA - 中文开源技术交流社区......

    本算法是参照李晓磊博士的论文实现的,详细的算法原理可阅读<一种新型的智能优化方法_人工鱼群算法_李晓磊> 算法基于鱼群的生存行为:在一片水域中,鱼存在的数目最多的地方就是本水域中富含营养物 ...

  9. python 加权随机算法_加权随机算法 - 飞儿飞的个人空间 - OSCHINA - 中文开源技术交流社区...

    加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项.这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10 ...

最新文章

  1. 在Eclipse中制作SSH配置文件提示插件
  2. Knockout应用开发指南 第八章:简单应用举例(1)
  3. 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )
  4. Xcode Git 使用
  5. 工业用微型计算机(24)-汇编语言分段结构
  6. SpringMVC-学习笔记03【SpringMVC常用注解】
  7. Arduino 各种模块篇 步进电机 step motor( 不用库,不用shield, 纯)
  8. Python中的回文详解
  9. 使用linux实验总结及心得,实验心得
  10. 在 vCenter Server 中出现报错:虚拟机无法进入受 vSphere HA 保护的状态 (2020082)
  11. pandas选取特定段
  12. iOS p12证书获取
  13. 解决:蓝奏云下载链接没法打开问题
  14. 应用市场显示服务器错误的是,win10应用商店打不开服务器出错怎么办
  15. Blender建模汇总
  16. PIL库改变图片大小
  17. win32,win64编程永恒;语言编程需要注意的64位
  18. 用于科学计算机的计算器特点是什么,科学计算器有哪些功能
  19. 数字通信系统中的频带利用率
  20. Cronlog日志分割器

热门文章

  1. 如何把游戏BGM文件夹中的.bank文件转换为.wav或者.mp3
  2. python课程设计 文字游戏 魔塔1
  3. 关于网页中的万年历(功能超强版)
  4. 【2017-2018】——酒已备好
  5. 小程序设置分享副标题方法
  6. LOGO 设计趋势?在这里学习跟上专业小伙伴的脚步
  7. Linux命令 - rmdir命令
  8. gb酱油和gbt酱油哪个好_GB2717-2018食品安全国家标准【酱油】.pdf
  9. 梅州英才外语学校计算机,梅州英才外语学校
  10. 汇编c语言混合编程pdf,C语言及汇编语言混合编程的方法.pdf