AK F.*ing leetcode 流浪计划之多点共面(线)

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

本期话题,如何判断多点共线,多点共面

如何判断3点共线

采用向量法判断

向 量 对 于 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=(y1z2z1y2,z1x2x1z2,x1y2y1x2)

如 向 量 X 是 一 个 0 向 量 , 则 说 明 V 1 , V 2 , 共 线 , 对 应 向 量 的 起 终 点 也 共 线 。 如向量X是一个0向量,则说明V_1,V_2, 共线,对应向量的起终点也共线。 X0V1,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,x1y2y1x2),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) 3P1(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)的叉积即可。 (P2P1),(P3P1)

如何判断多点共面

基本思想,先三点(不共线)确定一个平面,然后将第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(z2z3)+y2(z3z1)+y3(z1z2)

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(x2x3)+z2(x3x1)+z3(x1x2)

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(y2y3)+x2(y3y1)+x3(y1y2)

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(y2z3y3z2)x2(y3z1y1z3)x3(y1z2y2z1)

点法式

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=(P2P1)X(P3P1)=(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=(AP1x+BP1y+CP1z)

注意点

有可能给出的点都共线,可以先用共线方法判断是否共线,如果没有。
则选择三个不共线的点组成一个平面。
得到方程就可以将剩下的点代入验证。

代码实现

#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/

在这里插入图片描述

你可能感兴趣的:(图形学,leetcode,leetcode,线性代数,计算几何)