前言

我总结了一下向量点乘,叉乘的概念,以及他们的应用及相关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;
}

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

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

  1. Vjudge - E - 这是高中数学向量题

    2017-07-15 22:29:06 writer:pprp 评价,用到了叉乘,很麻烦,C++构造知识必须扎实 题目如下: 我们用逆时针方向的顶点序列来表示,我们很想了解这块地的基本情况,现在请你编 ...

  2. C++实现,拓展中国剩余定理——解同余方程组(理论证明和代码实现)

    拓展中国剩余定理 前言 记得半年前还写过关于拓展中国剩余定理的博客...不过那时对其理解还不是比较深刻,写的也比较乱. 于是趁学校复习之机,再来重温一下拓展中国剩余定理(以下简称ExCRT) 记得半年 ...

  3. binary-heap(二叉堆)原理及C++代码实现

    二叉堆可以看做一个近似的完全二叉树,所以一般用数组来组织. 二叉堆可以分为两种形式:最大堆和最小堆.最大堆顾名思义,它的每个结点的值不能超过其父结点的值,因此堆中最大元素存放在根结点中.最小堆的组织方 ...

  4. BinarySearchTree(二叉搜索树)原理及C++代码实现

    BST是一类用途极广的数据结构.它有如下性质:设x是二叉搜索树内的一个结点.如果y是x左子树中的一个结点,那么y.key<=x.key.如果y是x右子树中的一个结点,那么y.key>=x. ...

  5. 强化学习中REIINFORCE算法和AC算法在算法理论和实际代码设计中的区别

    背景就不介绍了,REINFORCE算法和AC算法是强化学习中基于策略这类的基础算法,这两个算法的算法描述(伪代码)参见Sutton的reinforcement introduction(2nd). A ...

  6. python基础-模块(全是理论,没有代码)

    模块 概念:一系列功能的结合体.相当于模块包着一堆函数与代码.本质上是py文件. 来源: python内置的模块----→ python解释器的模块 第三方的模块 -----→ 其他人编写提供的 自定 ...

  7. 关于导入web项目之后项目名上有红叉,但是能够正常运行,代码不会报错的问题

    解决方式之一: 1.进入项目包下的.settings目录 2.找到org.eclipse.wst.common.project.facet.core.xml文件,用记事本打开 3.将<runti ...

  8. 【十天自制软渲染器】DAY 03:画一个三角形(向量叉乘算法 & 重心坐标算法)

    如果你喜欢我写的文章,可以把我的公众号设为星标 ,这样每次有更新就可以及时推送给你啦. 前面两天画了点和线,今天我们来画一个最简单也是最强大的面--三角形. 本文主要讲解三角形绘制算法的推导和思路(只 ...

  9. matlab cross 3*1 向量叉乘

    一定是1*3  或者3*1 的向量才可以叉乘 A=[1 2 3] B=[4 5 6] cross(A,B) ans=[-3 6 -3] 解决机器人微分运动量之间的等价关系

  10. nyoj-952-最大四边形 (向量叉乘)

    题目链接 /* Name:nyoj-952-最大四边形 Copyright: Author: Date: 2018/4/27 10:46:24 Description: 枚举一条对角线,再选择一个 看 ...

随机推荐

  1. iview Input 必填不能未空 不能输入空格 v-model.trim required: true

    iview Input 必填不能未空 不能输入空格 v-model.trim required: true 需求 测试在验证必填的时候,会输入一个空格,本着空格不算内容的原则,会提一个bug 解决方案 ...

  2. end_of_line = lf 选择行尾序列 .editorconfig - 老项目不动代码存盘 文件变动 CRLF 的问题 vscode

    end_of_line = lf 选择行尾序列 .editorconfig - 老项目不动代码存盘 文件变动 CRLF 的问题 缘由 vscode 老项目代码,没有变动,ctrl + s后 文件有变化 ...

  3. 【线段树】【leetcode 729. 我的日程安排表 I】

    class MyCalendar { class Seg { int l; int r; boolean val; Seg left; Seg right; public Seg(int x, int ...

  4. openApi generator总是生成类名为 defaultApi

    生成器可以开启 useTags 设置,开启之后会根据 api 文档中的 tags 生成前缀类名,因此,要不生成 defaultApi 需要以下操作: 1.openApi 文档中每个 url 必须要有 ...

  5. 记录--开始使用Vue 3时应避免的10个错误

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 Vue 3 稳定已经有一段时间了.许多代码库正在生产中使用它,其他人最终也必须进行迁移.我有机会与它一起工作,并记录了我的错误,这可能是你 ...

  6. 面试官:只知道v-model是:modelValue和@onUpdate语法糖,那你可以走了

    前言 我们每天都在用v-model,并且大家都知道在vue3中v-model是:modelValue和@update:modelValue的语法糖.那你知道v-model指令是如何变成组件上的mode ...

  7. JAVA 获取文件 MD5

    private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); // 文件类取MD5 public ...

  8. CSP:Object as Point同会议论文,相似思想用于人脸和行人检测 | CVPR 2019

    CSP将目标定义为中心点和尺寸,通过网络直接预测目标的中心和寸尺,相对于传统的RCNN类型检测算法轻量化了不少.整体思想与Object as Points撞车了,真是英雄所见略同   来源:晓飞的算法 ...

  9. Kingbase Create Type 语句使用

    Kingbase create type类型创建语句 说明: KingbaseES 数据库中,可以通过CREATE TYPE语句定义一种新的数据类型. 有八种形式的CREATE TYPE.它们分别创建 ...

  10. KingbaseES KWR中等待事件分析案例

    背景 昨天有现场同事碰到了一个现象,一条简单的update语句运行缓慢.单独运行没有问题,在特定时间运行就会非常缓慢,怀疑是业务系统特殊逻辑导致数据库有阻塞引发的update语句慢的现象.故此现场同事 ...