欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。
本期话题,如何判断多点共线,多点共面
采用向量法判断
向 量 对 于 V 1 = ( x 1 , y 1 , z 1 ) , V 2 = ( x 2 , y 2 , z 2 ) 向量对于V_1=(x_1, y_1,z_1), V_2=(x_2, y_2,z_2) 向量对于V1=(x1,y1,z1),V2=(x2,y2,z2)
叉 积 X = V 1 V 2 = ( y 1 ∗ z 2 − z 1 ∗ y 2 , z 1 ∗ x 2 − x 1 ∗ z 2 , x 1 ∗ y 2 − y 1 ∗ x 2 ) 叉积X=V_1V_2= (y_1*z_2 - z_1*y_2, z_1*x_2 - x_1*z_2, x_1*y_2 - y_1*x_2) 叉积X=V1V2=(y1∗z2−z1∗y2,z1∗x2−x1∗z2,x1∗y2−y1∗x2)
如 向 量 X 是 一 个 0 向 量 , 则 说 明 V 1 , V 2 , 共 线 , 对 应 向 量 的 起 终 点 也 共 线 。 如向量X是一个0向量,则说明V_1,V_2, 共线,对应向量的起终点也共线。 如向量X是一个0向量,则说明V1,V2,共线,对应向量的起终点也共线。
对 于 二 维 平 面 叉 积 是 X = ( 0 , 0 , x 1 ∗ y 2 − y 1 ∗ x 2 ) , 只 要 判 断 最 后 一 项 是 否 为 0 即 可 对于二维平面叉积是X=(0,0,x_1*y_2 - y_1*x_2), 只要判断最后一项是否为0即可 对于二维平面叉积是X=(0,0,x1∗y2−y1∗x2),只要判断最后一项是否为0即可
给 出 3 点 P 1 ( x 1 , y 1 , z 1 ) , P 2 ( x 2 , y 2 , z 2 ) , P 3 ( x 3 , y 3 , z 3 ) 给出3点P_1(x_1,y_1,z_1), P_2(x_2,y_2,z_2),P_3(x_3,y_3,z_3) 给出3点P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)
只 要 判 断 向 量 ( P 2 − P 1 ) , ( P 3 − P 1 ) 的 叉 积 即 可 。 只要判断向量(P_2-P_1),(P_3-P_1)的叉积即可。 只要判断向量(P2−P1),(P3−P1)的叉积即可。
基本思想,先三点(不共线)确定一个平面,然后将第4个点代入平面方程验证。
平 面 方 程 A x + B y + C y + D = 0 平面方程 Ax+By+Cy+D=0 平面方程Ax+By+Cy+D=0
三个不共线的点确定一个平面有2种方法:1.行列式法,2.点法式
设 三 点 为 : P 1 ( x 1 , y 1 , z 1 ) , P 2 ( x 2 , y 2 , z 2 ) , P 3 ( x 3 , y 3 , z 3 ) 设三点为:P_1(x_1,y_1,z_1), P_2(x_2,y_2,z_2),P_3(x_3,y_3,z_3) 设三点为:P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)
将三点代入方程得到三个方程
通过行列式法求出
A = ∣ 1 y 1 z 1 1 y 2 z 2 1 y 3 z 3 ∣ A=\begin{vmatrix}1 &y_1 &z_1 \\ 1 &y_2 &z_2\\1 &y_3 &z_3 \end{vmatrix} A=∣∣∣∣∣∣111y1y2y3z1z2z3∣∣∣∣∣∣
B = ∣ x 1 1 z 1 x 2 1 z 2 x 3 1 z 3 ∣ B=\begin{vmatrix}x_1 &1 &z_1 \\ x_2 &1 &z_2\\x_3 &1 &z_3 \end{vmatrix} B=∣∣∣∣∣∣x1x2x3111z1z2z3∣∣∣∣∣∣
C = ∣ x 1 y 1 1 x 2 y 2 1 x 3 y 3 1 ∣ C=\begin{vmatrix}x_1 &y_1 &1 \\ x_2 &y_2 &1\\x_3 &y_3 &1 \end{vmatrix} C=∣∣∣∣∣∣x1x2x3y1y2y3111∣∣∣∣∣∣
D = − ∣ x 1 y 1 z 1 x 2 y 2 z 2 x 3 y 3 z 3 ∣ D=-\begin{vmatrix}x_1 &y_1 &z_1 \\ x_2 &y_2 &z_2\\x_3 &y_3 &z_3 \end{vmatrix} D=−∣∣∣∣∣∣x1x2x3y1y2y3z1z2z3∣∣∣∣∣∣
A = y 1 ( z 2 − z 3 ) + y 2 ( z 3 − z 1 ) + y 3 ( z 1 − z 2 ) A=y_1(z_2-z_3)+y_2(z_3-z_1)+y_3(z_1-z_2) A=y1(z2−z3)+y2(z3−z1)+y3(z1−z2)
B = z 1 ( x 2 − x 3 ) + z 2 ( x 3 − x 1 ) + z 3 ( x 1 − x 2 ) B=z_1(x_2-x_3)+z_2(x_3-x_1)+z_3(x_1-x_2) B=z1(x2−x3)+z2(x3−x1)+z3(x1−x2)
C = x 1 ( y 2 − y 3 ) + x 2 ( y 3 − y 1 ) + x 3 ( y 1 − y 2 ) C=x_1(y_2-y_3)+x_2(y_3-y_1)+x_3(y_1-y_2) C=x1(y2−y3)+x2(y3−y1)+x3(y1−y2)
D = − x 1 ( y 2 ∗ z 3 − y 3 ∗ z 2 ) − x 2 ( y 3 ∗ z 1 − y 1 ∗ z 3 ) − x 3 ( y 1 ∗ z 2 − y 2 ∗ z 1 ) D=-x_1(y_2*z_3-y_3*z_2)-x_2(y_3*z_1-y_1*z_3)-x_3(y_1*z_2-y_2*z_1) D=−x1(y2∗z3−y3∗z2)−x2(y3∗z1−y1∗z3)−x3(y1∗z2−y2∗z1)
P = ( P 2 − P 1 ) X ( P 3 − P 1 ) = ( P x , P y , P z ) P=(P2-P1)X(P3-P1)=(P_x, P_y, P_z) P=(P2−P1)X(P3−P1)=(Px,Py,Pz)
则 P 为 平 面 的 法 向 量 则P为平面的法向量 则P为平面的法向量
A = P . x , B = P . y , C = P . z A=P.x, B=P.y, C=P.z A=P.x,B=P.y,C=P.z
将 P 1 代 入 方 程 将P_1代入方程 将P1代入方程
D = − ( A ∗ P 1 x + B ∗ P 1 y + C ∗ P 1 z ) D=-(A*P_{1x}+B*P_{1y}+C*P_{1z}) D=−(A∗P1x+B∗P1y+C∗P1z)
有可能给出的点都共线,可以先用共线方法判断是否共线,如果没有。
则选择三个不共线的点组成一个平面。
得到方程就可以将剩下的点代入验证。
#include
#include
using namespace std;
class Point {
public:
int x, y, z;
Point(int xx, int yy, int zz) : x(xx), y(yy), z(zz) {}
Point() {}
Point operator-(Point &a) const {
return Point(x - a.x, y - a.y, z - a.z);
}
};
bool isZero(Point p) {
return p.x == 0 && p.y == 0 && p.z == 0;
}
Point xmult(Point p1, Point p2) {
return Point(p1.y * p2.z - p1.z * p2.y, p1.z * p2.x - p1.x * p2.z, p1.x * p2.y - p1.y * p2.x);
}
// 判断多点是否共面,没有重复的点,行列式法
/*
* 平面方程:Ax+By+Cz+D=0
* 先选出不共线三点计算出A,B,C,否则如果不存在非共线的点,则都在一平面上。
* 设有三点P1(x1,y1,z1), P2(x2,y2,z2), P3(x3,y3,z3)
* A=y1(z2-z3)+y2(z3-z1)+y3(z1-z2)
* B=z1(x2-x3)+z2(x3-x1)+z3(x1-x2)
* C=x1(y2-y3)+x2(y3-y1)+x3(y1-y2)
* D=-x1(y2z3-y3z2)-x2(y3z1-y1z3)-x3(y1z2-y2z1)
* 将其他点代入平面方程,检查是否为0即可
*/
bool IsInPlane(vector points) {
int d3 = -1;
for (int i = 2; i < points.size(); ++i) {
Point c = xmult(Point(points[1].x - points[0].x, points[1].y - points[0].y, points[1].z - points[0].z),
Point(points[i].x - points[0].x, points[i].y - points[0].y, points[i].z - points[0].z));
if (!isZero(c)) {
d3 = i;
break;
}
}
// 所有点都共线
if (d3 == -1) {
return true;
}
vector planePoints = {points[0], points[1], points[d3]};
// 计算ABCD
int A = 0;
for (int i = 0; i < 3; ++i) {
A += planePoints[i].y * (planePoints[(i + 1) % 3].z - planePoints[(i + 2) % 3].z);
}
int B = 0;
for (int i = 0; i < 3; ++i) {
B += planePoints[i].z * (planePoints[(i + 1) % 3].x - planePoints[(i + 2) % 3].x);
}
int C = 0;
for (int i = 0; i < 3; ++i) {
C += planePoints[i].x * (planePoints[(i + 1) % 3].y - planePoints[(i + 2) % 3].y);
}
int D = 0;
for (int i = 0; i < 3; ++i) {
D -= planePoints[i].x *
(planePoints[(i + 1) % 3].y * planePoints[(i + 2) % 3].z -
planePoints[(i + 2) % 3].y * planePoints[(i + 1) % 3].z);
}
// 检查剩余点
for (int i = 0; i < points.size(); ++i) {
if (A * points[i].x + B * points[i].y + C * points[i].z + D != 0) {
return false;
}
}
return true;
}
// 判断多点是否共面,没有重复的点, 法点式
/*
* 平面方程:Ax+By+Cz+D=0
* 先选出不共线三点计算出A,B,C,否则如果不存在非共线的点,则都在一平面上。
* 设有三点P1(x1,y1,z1), P2(x2,y2,z2), P3(x3,y3,z3)
* P=xmult(P2-P1, P3-P1), 求叉积得到法向
* A=P.x, B=P.y, C=P.z, D=-P*P1
* 将其他点代入平面方程,检查是否为0即可
*/
bool IsInPlane2(vector points) {
int d3 = -1;
for (int i = 2; i < points.size(); ++i) {
Point c = xmult(points[1] - points[0], points[i] - points[0]);
if (!isZero(c)) {
d3 = i;
break;
}
}
// 所有点都共线
if (d3 == -1) {
return true;
}
Point P = xmult(points[1] - points[0], points[d3] - points[0]);
int D = -(P.x * points[0].x + P.y * points[0].y + P.z * points[0].z);
// 检查剩余点
for (int i = 0; i < points.size(); ++i) {
if (P.x * points[i].x + P.y * points[i].y + P.z * points[i].z + D != 0) {
return false;
}
}
return true;
}
vector> getTests() {
vector> ans;
ans.push_back({{1, 2, 0},
{3, 4, 0}});
ans.push_back({{1, 0, 0},
{2, 0, 0},
{3, 0, 0},
{4, 0, 0}});
ans.push_back({{1, 2, 0},
{3, 4, 0},
{4, 5, 0},
{6, 7, 0},
{6, 10, 0}});
ans.push_back({{1, 2, 0},
{3, 4, 0},
{4, 5, 0},
{100, 7, 1}});
ans.push_back({{1, 2, 10},
{3, 4, 1},
{4, 5, 2},
{100, 7, 10}});
ans.push_back({{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{2, -1, 0}});
ans.push_back({{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{2, -1, 1}});
return ans;
}
int main(void) {
auto tests = getTests();
for (auto v:tests) {
cout << "p" << endl;
cout << IsInPlane(v) << endl;
cout << IsInPlane2(v) << endl;
}
return 0;
}
取面上一点与目标点形成向量,求向量在法向上的投影是否为0。
https://leetcode-cn.com/problems/max-points-on-a-line/
https://leetcode-cn.com/problems/check-if-it-is-a-straight-line/