【学习笔记】【数学】计算几何基础

发布时间 2023-08-08 17:01:40作者: Sonnety
点击查看目录

前置知识:

建议虽然是简单的前置知识,还是打开略过一遍。

  • 浮点数与误差分析(少用除法)

  • 向量相关

向量

向量,就是带有方向和大小两个属性的边,通常形式为\(\overrightarrow{AB}=(a_1,a_2)=A\)

运算与性质:

  • 判等:两点坐标重合。
il int dcmp(double a){
    if(a<-eps)	return -1;
    if(a>eps)	return 1;
    return 0;
}
il bool operator==(node a,node b)<% return !dcmp(a.x-b.x) && !dcmp(a.y-b.y); %>
  • 加减:\(A+B=(a_1,a_2)+(b_1,b_2)=(a_1+b_1,a_2+b_2)\)
il node operator+(node a,node b)<% return node(a.x+b.x,a.y+b.y); %>
il node operator-(node a,node b)<% return node(a.x-b.x,a.y-b.y); %>
  • 模长:\(|A|=\sqrt{a_1^2+a_2^2}\)
il double len(node a)<% return sqrt(dot(a,a)); %>
//dot是点乘函数
  • 角度:\(\tan \theta =\frac{a_2}{a_1},\theta =arctan \frac{a_2}{a_1}\)

  • 数乘:\(k\times A=k\times (a_1,a_2)=(ka_1,ka_2)\)

il node operator*(node a,double k)<% return node(a.x*k,a.y*k); %>
  • 点乘:\(A\cdot B=(a_1,a_2)\cdot (b_1,b_2)=a_1b_1+a_2b_2\)

(几何意义:\(A\) 乘上 \(B\)\(A\) 上的投影,即\(|A|\times |B|\times cos \Theta\))

il double dot(node a,node b)<% return a.x*b.x+a.y*b.y; %>
  • 夹角:\(\cos \theta=\frac{A\cdot B}{|A|\times |B|}\)
il double angle(node a,node b)<% return acos(dot(a,b)/len(a)/len(b)); %>
  • \(A\cdot B=0\),(\(A,B\) 不为0)则 \(A\)\(B\) 垂直,\(>0\) 为锐角,\(<0\) 为钝角。

  • 叉积:\(A\times B=\begin{vmatrix} a_1&b_1\\a_2&b_2 \end{vmatrix}=a_1b_2-b_1a_2=-B\times A\)

(几何意义:叉积的绝对值=面积)

il double cro(node a,node b)<% return a.x*b.y-a.y*b.x; %>
  • 面积:\(|A\times B|=|A||B|\sin \theta\)

  • 旋转:将 \(A\) 旋转 \(\alpha\) 弧度得到 \(A'\)

\(A=(a_1,a_2)=r(\cos\theta,\sin\theta);\)

\(A'=r(\cos(\theta+\alpha),\sin(\theta+\alpha)=r(\cos\theta \cos\alpha-\sin\theta \sin\alpha,\cos\theta \sin\alpha+sin\theta \cos\alpha)\)

\(=(a_1 \cos\alpha-a_2 \sin\alpha,a_1 \sin\alpha+a_2 \cos\alpha)\)

  • 直线与线段相关
直线与线段

直线一般形式有四:

\[ \begin{aligned} &ax+by+c=0\\ &y=kx+b\\ &两个端点\\ &一个起点和一个向量 \end{aligned} \]

其中,\(k\) 是直线的斜率,\(k=\frac{y_2-y_1}{x_2-x_1}=tan \alpha\)\(b\) 是直线的截距。

特别地,当两条直线垂直时有 \(k1\times k2=-1\)

而线段,在代码中往往可以这样表示:

struct Node{double x,y;};
struct line{node p1,p2;};

有以下运算:

  • 判断点 \(P\) 是否在直线 \(AB\) 上。
il int judge_LINE(Node p,Node a,Node b)<% return !mystd::dcmp(Vector::cro(p-a,b-a)); %>
  • 判断点 \(P\) 是否在线段 \(AB\) 上。
il int judge_line(Node p,Node a,Node b)<% return !mystd::dcmp(Vector::cro(p-a,b-a)) && mystd::dcmp(mystd::fMin(a.x,b.x)-p.x)<=0 && mystd::dcmp(mystd::fMax(a.y,b.y)-p.y)<=0; %>
  • 求点 \(P\) 到直线 \(AB\) 的垂足。
il Node footnode(Node p,Node a,Node b){
		Node x=p-a,y=p-b,z=b-a;
		double len1=Vector::dot(x,z)/Vector::len(z),len2=-1.0*Vector::dot(y,z)/Vector::len(z);
		return a+z*(len1/(len1+len2));
	}

证明:见图。

  • 求点 \(P\) 到直线 \(AB\) 的对称点:
il Node symmetry(Node p,Node a,Node b)<% return p+(footnode(p,a,b)-p)*2; %>

叉积与跨立实验

我们在前置知识中已经提到叉积,但是叉积的什么“右手定则”我不会,长大再学(

上面提到,对于向量 \(A(x_1,y_1),B(x_2,y_2)\),有叉积:\(x_1y_2-x_2y_1\)

(下有证明,别急)

\(p_0\) 为参考点,有代码:

叉积
double multi(node p1, node p2, node p0) {
    double x1, y1, x2, y2;
    x1 = p1.x - p0.x;
    y1 = p1.y - p0.y;
    x2 = p2.x - p0.x;
    y2 = p2.y - p0.y;
    return x1 * y2 - x2 * y1;
}

而该函数返回值若大于 \(0\),证明 \(p_2\)\(p_1\) 逆时针方向,小于 \(0\) 则在顺时针方向,若等于 \(0\) 则共线。

为什么?我们现在来证明这个公式。

\[ \begin{aligned} x_1y_2-x_2y_1\\ &=(|A|\cos\theta_1)(|B|\sin\theta_2)-(|A|\sin\theta_1)(|B|\cos\theta_2)\\ &=|A||B|\cos\theta_1\sin\theta_2-|A||B|\sin\theta_1\cos\theta_2\\ &=|A||B|\sin(\theta_2-\theta_1)\\ &=-|A||B|\sin\theta \end{aligned} \]

所以,当 \(x_1y_2-x_2y_1>0\),意味着 \(\sin\theta<0\),就是 \(B\)\(A\) 的逆时针方向。