首页 > 编程语言 >【数学】向量点乘、叉乘的理论、应用及代码实现(C++)

【数学】向量点乘、叉乘的理论、应用及代码实现(C++)

时间:2024-04-14 17:11:20浏览次数:23  
标签:AB return Point Vector3d 代码 C++ vec 向量

前言

我总结了一下向量点乘,叉乘的概念,以及他们的应用及相关C++代码的实现。blog
这类问题也是技术面试经常碰到的,一次研究透了会有收获。

1 向量

向量具有大小方向
共线向量:两个平行的向量为共线向量。

1.1 叉积 Cross Product

$$\vec{a}\times\vec{b}=|\vec{a}||\vec{b}|\sin{\theta}\vec{n}$$

$\theta$是两个向量之间的角度,$\vec{n}$是与两个向量都垂直的单位向量,方向遵循右手定则(右手食指从$\vec{a}$划到$\vec{b}$,大拇指的方向)。

两个向量的叉积结果是一个与两者都垂直的向量。叉积的幅度值大小等于由这两个向量为边组成的平行四边形的面积。当两个向量垂直时,大小也达到最大,及矩形的面积。(这个特性决定了他可以用来计算空间中一个点到一个直线的距离,利用几何中平行四边形的面积同时等于底乘高,后面会介绍。)

三维空间中,叉积的结果也可以用3x3矩阵的行列式表示。

$$
\vec{a}\times \vec{b}=\det(\vec{i},\vec{j},\vec{k};a_1,a_2,a_3;b_1,b_2,b_3)\
=(a_2b_3 - a_3,b_2)\vec{i}+(a_3b_1 - a_1,b_3)\vec{j}+(a_1b_2 - a_2,b_1)\vec{k}$$

1.2 点积 Dot Product

叉积给出一个向量结果,但点积给出一个标量结果。
它将向量的相同方向投影的的长度相乘,因此使用$\cos{\theta}$将其中一个向量投影到另一个上。所以如果两个向量成直角,那么结果为零。点积更容易理解一些。

$$
\vec{a}\cdot \vec{b} = |\vec{a}||\vec{b}|\cos{\theta}
$$

2 实际应用

  1. 判断两个向量是否:

    1. 共线:$\vec{A}$=k*$\vec{B}$,其中k是一个标量;叉积是零向量(仅适用于三维空间);对应坐标的比率相等。
    2. 垂直:点积为零。
  2. 计算点P在线段AB上的投影点C坐标。

    1. 向量$\vec{AB}$,$\vec{AP}$,点积结果为D。$\vec{AB} \cdot \vec{AP}=D$
    2. 推导一下:$\vec{AB} \cdot \vec{AP}=|\vec{AB}| |\vec{AP}| \cos{\theta}=|\vec{AC}| |\vec{AB}| = D$ -> $D/|\vec{AB}| = |\vec{AC}|$
    3. 求比率:$k = |\vec{AC}|/|\vec{AB}| = D/{|\vec{AB}|^2}$
    4. 最终坐标根据$A$和$k$可以求得:$C = A + k\vec{AB}$
  3. 如何验证C是投影点:

    1. 验证其共线性:$\vec{AC} = k \vec{AB}$或$\vec{AC} \times \vec{AB}=\vec{0}$
    2. 验证垂直:$\vec{PC}\cdot \vec{AB} = 0$
  4. 如何计算点P到线AB的距离d

    1. 叉积的范数是由两个向量张成的平行四边形的面积$(\vec{AB} \times \vec{AP})$
    2. 基于几何原理,这个面积也等于距离(高)乘以边长 $d * |\vec{AB}|$
    3. 所以$d = |\vec{AB} \times \vec{AP}|/|\vec{AB}|$

3 代码实现

第一个版本代码,不用额外的库,手搓一些Utility函数,透彻了解原理:

#include<iostream>
#include<cmath>
using namespace std;

struct Point
{
    double x, y, z;
    // Overloading the multiplication operator
    Point operator*(double k) const 
    {
        return {k*x, k*y, k*z};
    }
    Point operator+(Point A) const
    {
        return {A.x + x, A.y + y, A.z + z};
    }
    bool operator==(Point A) const
    {
        if (A.x == x and A.y == y and A.z == z)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

double dotProduct(Point A, Point B)
{
    return A.x * B.x + A.y * B.y + A.z * B.z;
}

Point crossProduct(Point A, Point B)
{
    return {A.y * B.z - A.z * B.y,
            A.x * B.z - A.z * B.x,
            A.x * B.y - A.y * B.x};
}

float calcNorm(Point A)
{
    return sqrt(A.x * A.x + A.y * A.y + A.z * A.z);
}

Point calcProjection(Point A, Point B, Point P)
{
    Point AB = {B.x - A.x, B.y - A.y, B.z - A.z};
    Point AP = {P.x - A.x, P.y - A.y, P.z - A.z};
    double dot_product = dotProduct(AB, AP);
    double k = dot_product / dotProduct(AB, AB);
    Point C = A + (AB * k);
    return C;
}

bool verifyProjection(Point A, Point B, Point P, Point C)
{
    Point AC = {C.x - A.x, C.y - A.y, C.z - A.z};
    Point AB = {B.x - A.x, B.y - A.y, B.z - A.z};
    Point PC = {C.x - P.x, C.y - P.y, C.z - P.z};
    double dot_product = dotProduct(PC, AB);
    Point cross_product = crossProduct(AC, AB);
    Point zero_vec = {0, 0, 0};
    if (dot_product == 0 and cross_product == zero_vec)
    {
        return true;
    }
    else
    {
        return false;
    }
}

float calcDistance(Point A, Point B, Point P)
{
    Point AB = {B.x - A.x, B.y - A.y, B.z - A.z};
    Point AP = {P.x - A.x, P.y - A.y, P.z - A.z};
    Point cross_product = crossProduct(AB, AP);
    float area_parallelogram = calcNorm(cross_product);
    return (area_parallelogram / calcNorm(AB));
}

int main()
{
    // Line segment AB
    Point A = {0, 0, 0};
    Point B = {4, 0, 0};
    // Point P
    Point P = {5, 8, 0};
    // Project P to AB and get point C
    Point C = calcProjection(A, B, P);
    cout << "Projection Point C: (" << C.x << ", " << C.y << ", " << C.z << ")" << endl;
    if (verifyProjection(A, B, P, C))
    {
        cout << "Correct!" << endl;
    }
    else
    {
        cout << "Incorrect." << endl;
    }
    cout << "Distance from P to AB is: " << calcDistance(A, B, P) << endl;
    return 0;
}

另外一个版本,通过使用Eigen库来避免自己写Utility函数,行数大大减少(君子生非异也,善假于物也。):

#include <iostream>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;

Vector3d calcProjection(Vector3d A, Vector3d B, Vector3d P)
{
    Vector3d AB = B - A;
    Vector3d AP = P - A;
    float AC_norm = AB.dot(AP) / AB.norm();
    Vector3d C = A + AC_norm / AB.norm() * AB;
    return C;
}

bool verifyProjection(Vector3d A, Vector3d B, Vector3d P, Vector3d C)
{
    Vector3d AB = B - A;
    Vector3d PC = P - C;
    Vector3d AC = C - A;
    Vector3d zero_vec = {0, 0, 0};
    Vector3d cross_product = AB.cross(AC);
    float dot_product = PC.dot(AB);
    if (dot_product == 0 and cross_product == zero_vec)
    {
        return true;
    }
    else
    {
        return false;
    }
}

float calcDistance(const Vector3d A, const Vector3d B, const Vector3d P)
{
    Vector3d AB = B - A;
    Vector3d AP = P - A;
    Vector3d cross_product = AB.cross(AP);
    float area_parallelogram = cross_product.norm();
    return area_parallelogram / AB.norm();
}

int main()
{
    Eigen::Vector3d A = {0, 0, 0};
    Eigen::Vector3d B = {2, 0, 0};
    Eigen::Vector3d P = {1, 3, 0};

    Vector3d C = calcProjection(A, B, P);
    cout << "Projection point is: " << C.x() << ", " << C.y() << ", " << C.z() << endl;
    if (verifyProjection(A, B, P, C))
    {
        cout << "Verification passes!" << endl;
    }
    else
    {
        cout << "Verification failed." << endl;
    }
    cout << "Distance from P to AB is: " << calcDistance(A, B, P) << endl;
}

有问题欢迎一起评论交流,我会回复或更新文章,谢谢!

标签:AB,return,Point,Vector3d,代码,C++,vec,向量
From: https://www.cnblogs.com/MengWoods/p/18134370

相关文章

  • 如何阅读代码
    转自:https://www.codedump.info/post/20200605-how-to-read-code-v2020/https://www.zhihu.com/question/21186887,https://boholder.github.io/blogs/learn-from-source-code/1.介绍 大多数程序员只在少数特别领域编过程。一般而言,如果你不时常推自己一把,你的编程技能会维持......
  • blender使用python创建立方体并应用变换代码
    importbpy#定义咖啡馆的尺寸cafe_width=10#宽度,单位:米cafe_length=15#长度,单位:米cafe_height=3#高度,单位:米#创建咖啡馆的主体(立方体)bpy.ops.mesh.primitive_cube_add(size=10,enter_editmode=False,location=(0,0,0))#获取新创建的立方体对象......
  • C++U6-12-阶段复习测评
          7、贝尔曼福特算法,是按顺序一轮一轮的松弛,如果有可以松弛的那就再来一轮;这个题第二轮就没有可以松弛的了,所以就没有第3轮了 8、这题是dijkstra算法,算法逻辑是:Dijkstra最短路径算法的步骤如下:初始化:创建一个距离数组 dist,用于存储起点到每个节点......
  • 代码随想录算法训练营第8天 | 字符串 344.反转字符串 541. 反转字符串II 卡码网:54.
    leetcode344.反转字符串题目344.反转字符串编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组s的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。解题思路实现代码......
  • 代码随想录算法训练营第9天 | 字符串(KMP算法) 28. 找出字符串中第一个匹配项的下标
    leetcode28.找出字符串中第一个匹配项的下标题目28.找出字符串中第一个匹配项的下标给你两个字符串haystack和needle,请你在haystack字符串中找出needle字符串的第一个匹配项的下标(下标从0开始)。如果needle不是haystack的一部分,则返回-1。解题思路实现代......
  • 本地升级idea后,不能向github上提交代码问题处理
    问题现象:本人自己电脑之前一直使用idea2018.1商业破解版,之前有简历本地代码仓库,并在github上建立了关联的远程代码仓库。最近本人在本地升级一下idea,从idea2018.1商业版升级到2023.1.5社区版本(idea支持win7的版本基本就到2023.1这个版本了,目前本人尝试安装了2023.1.5和2023.1.3......
  • 01_C++基础
    C++基础1.什么是c++。​c++是c语言的升级版,在c的基础上增加了很多功能。是一种高级语言。2.什么是面向对象,什么又是面向过程。​c语言就是面向过程的,c++就是面向对象的。举例:a+b​直接计算a+b就是面向过程。​面向对象就是给a+b穿上了一层衣服。不......
  • 代码随想录算法训练营第7天 | 哈希表 454.四数相加II 383. 赎金信 15. 三数之和 18.
    leetcode454.四数相加II题目454.四数相加II解题思路实现代码leetcode383.赎金信题目383.赎金信解题思路实现代码leetcode15.三数之和题目15.三数之和解题思路实现代码leetcode454.四数相加II题目18.四数之和解题思路实现代码......
  • c++ inline
    当在头文件中定义函数时,如果这个头文件被多个.cpp文件包含,那么每个包含该头文件的.cpp文件都会有一个该函数的副本。这在链接阶段会引起“多重定义”的错误,因为链接器找到多个相同符号的定义。使用inline关键字可以解决这个问题。当一个函数被声明为inline,编译器会尝试将......
  • MySQL 8 显示错误代码2058
    在使用mysql-uroot-P3307-proot和SQLyong连接MySQL时报错1.在MySQL8.3CommandLineClient登录点击查看代码Enterpassword:****WelcometotheMySQLmonitor.Commandsendwith;or\g.YourMySQLconnectionidis14Serverversion:8.3.0MySQLCommunitySer......