一点点计算几何:向量的入门

11490DX Re: Master Lv.15

 

学好了数学,你就会成为根神!数学乃万物之根,掌握了数学就掌握了一切!


定义

首先介绍一下点:

点可以用二维坐标 表示,可以用结构体 Point 存储。

1
2
3
struct Point{
int x, y;
};

有大小并且有方向的量叫向量。我们可以把他看作连接了两个点。

同时,为了方便,我们可以记录一个坐标,就相当于是从 指向

如图,一个从点 连向 的向量可以看作从 连向 的向量。所以为了方便,我们可以直接用 表示这个向量。因为这两个向量在平面上其实是相等的。

于是为了方便写代码,我们可以执行这个操作:

1
typedef Point Vector;

四则运算

  • 加法运算

点加点显然是没有意义的。而向量加向量是有意义的。就相当于是把后一个向量的起点给放在前一个向量的终点上,然后求后一个向量的终点的坐标。

例如此图,,那么

那么就相当于是把两个向量的横坐标加起来当作新的横坐标,两个向量的纵坐标当作新的纵坐标。

1
2
3
Vector operator + (const Vector &a, const Vector &b){
return {a.x + b.x, a.y + b.y};
}
  • 减法运算

几何意义上的向量的减法运算就是:

如果你要求 ,那么先让 起点重合, 就是从 的终点连向 的终点的一个向量。

代数意义上, 的横坐标就是 的横坐标减去 的横坐标, 的纵坐标就是 的纵坐标减去 的纵坐标。这个减法和普通的减法一样,都有顺序!!

1
2
3
Vector operator - (const Vector &a, const Vector &b){
return {a.x - b.x, a.y - b.y};
}
  • 数乘运算

一个向量乘以一个数 就是相当于是把这个向量放大到他的 倍。

1
2
3
Vector operator * (const Vector &a, const int &b){
return {a.x * b, a.y * b};
}
  • 除法运算

一个向量除以一个数 就是相当于是把这个向量缩小到他的 。(实际运算的时候要根据精度将点的类型改成 doublelong double 等等)

1
2
3
Vector operator / (const Vector &a, const int &b){
return {a.x / b, a.y / b};
}

点积与叉积

点积和叉积都是向量乘以向量,但是它们不同。这两个名字其实是根据两个不同的 “乘法符号” 命名的,点积就是 ,叉积就是 。非常的生动形象啊。

点积

点积的结果是一个数值。运算是这样的:

如图,。于是我们得出了他的几何意义: 就是 上的投影长度乘以 的模长。

这个夹角 是两个向量的两个角里面最小的那一个。

而有一种更简单的算法:

考虑证明:

根据这个东西我们可以看出来点积其实是有交换律的。

于是我们可以得出一些性质:

  • ,那么 的夹角为锐角。
  • ,那么 的夹角为钝角。
  • ,那么 的夹角为直角。

(其实看 可以直接判断出来

1
2
3
int Dot(Point u, Point v){
return u.x*v.x + u.y*v.y;
}

点积的应用

最直观地、可以求两个向量的夹角。

1
2
3
4
5
6
double Len(Vector a){
return sqrt(a.x*a.x+a.y*a.y);
}
double Angle(Vector u, Vector v){
return acos(Dot(u, v)/Len(u)/Len(v));
}

叉积

叉积的结果是也一个数值。运算是这样的:

和上面的点积形式比较像,就是把 改为了

但是注意:这里的 向量 u 逆时针旋转到向量 v 所经过的角度

所以,叉积有正有负。即:

  • 的逆时针方向时(左侧),那么
  • 的顺时针方向时(右侧),那么

  • 的方向相同或相反时(共线),那么

这个也可以从 的定义中看出来。

而叉积也有一个很好算的神奇公式:

所以可以看出,叉积没有交换律。叉积是有顺序的。 互为相反数。

他的几何意义:以向量 作为边形成的平行四边形的面积就是 绝对值

1
2
3
int Cross(Point u, Point v){
return u.x*v.y - u.y*v.x;
}

直线

众所周知,直线可以用以下几个方法表示:

  1. 用直线上的两个点的坐标表示,因为两点确定一条直线
  2. /
  3. 根据一个点和倾斜角确定
  4. 点向式: 为一个点, 是一个向量。
1
2
3
struct Line{
Point p1, p2; //两点确定一条直线
};

叉积的应用

  • 点和直线的位置关系

判断点 在直线 (其实是向量)的左边、右边或是点就在直线上。

这个可以有两种算法:

  1. 构造向量 和向量 ,然后用叉积的正负来判断方向。
1
2
3
4
5
6
int Relation(Point p, Line l){
int res = Cross(p-l.p1, p-l.p2);
if(res < 0) return 1; // p 在 l 的左侧
if(res > 0) return 2; // p 在 l 的右侧
return 0; // p 与 l 重合
}
  1. 构造向量 ,求 ,然后用叉积的正负判断方向。个人觉得这种方法更加直观。
1
2
3
4
5
6
int Relation(Point p, Line l){
int res = Cross(l.p2 - l.p1, p - l.p1);
if(res > 0) return 1; // p 在 l 的左侧
if(res < 0) return 2; // p 在 l 的右侧
return 0; // p 与 l 重合
}

例题:

神秘大三角

Tips

在向量运算的过程中,因为用的是 double 类型,所以判断相不相等的时候肯定是会有一些误差的。我们要规避掉。我们可以设置一个极小值 来减小这个误差。

可读性最高的方法是重载运算符。这里使用了重新写一个函数的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

const double eps = 1e-9;
int cmp(double x){
if(fabs(x) < eps) return 0; // x == 0
else if(x < eps) return -1; // x < 0
return 1; // x > 0
}
bool operator < (const)
struct Point{
...
bool operator == (const Point &b){
if(cmp(b.p2 - p2) == 0 && cmp(b.p1 - p1) == 0) return 1;
return 0;
}
...
}

于是入门就先到这里,我们来讲二维凸包。(后面可能会在入门里面再添加内容)

二维凸包的定义

二维凸包就相当于,通俗的讲,你在平面上有一些大头针,你有一个橡皮筋,你用一个橡皮筋把所有的这些大头针包在里面,那么这个橡皮筋的形状就可以粗略看作是这些点的凸包的形状。

上图中灰色的一个凸多边形就是这些蓝色点的凸包。它包住了所有的蓝点。

我们来介绍一个 的算法求一些点的二维凸包。

Andrew 算法求二维凸包

我们首先给这些点排序:按照 坐标从小到大排序,如果 坐标相等,那么就按照 坐标从小到大排序。

然后我们选定最左边的点(也就是 ),然后就可以开始造凸包了。

我们将我们遍历过的所有点压进一个栈里面,当遍历到一个新的点的时候,判断他和栈顶的两个点的位置关系,然后来判断删不删。我们依然来举例说明:

首先栈中不足 个点,压入点

这个时候,第三个点来了,我们让他和栈顶点和次栈顶点判断一下位置关系:

具体的操作是,让 栈顶的点 和 新点和次栈顶的点连成的直线 判断位置关系。如上图,我们发现栈顶点 在新点和次栈顶点做成的直线 的左侧,很明显,如果不把 给踢出去,那么就不是一个凸包了。我们就把这个栈 pop 一下,然后继续检测。直到符合条件或是栈中的元素小于 了,那么就把新点加进来。

注意要持续检测!

另一个例子,插入点 时的判断:

注意:当新点和栈顶点、次栈顶点共线时:

看题目要求。

当所有点都被遍历完了之后,我们看到了一个凸包的雏形:

注:这里的 是下凸包的点的个数,也是上凸包弹出元素时的边界。这 个元素必须保留在栈里面。

然后我们就可以开始再从后往前遍历一遍,来找上凸包。


找上凸包的过程其实和找下凸包的过程都是一样的,但是注意一下边界问题。

具体地说,弹出元素的操作只适用于当前栈元素大于 的情况。

因为你再弹出,你就把下凸包的右端点给弹出去了。

最后,我们得到了一个栈和一个凸包:

但是,我们发现这个凸包的点(即栈)里面有两个左端点。就判一下(设栈中元素个数为 ):

时,弹出栈顶元素。

于是我们就完美的得到了这个凸包了!


例题:

二维凸包模板

  • Title: 一点点计算几何:向量的入门
  • Author: 11490DX
  • Created at : 2024-12-07 00:00:00
  • Updated at : 2025-02-14 13:09:36
  • Link: https://11490dx.net/2024/12/07/vector/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments