点乘和叉乘及其物理意义(C++STL实现)

  1. 一些错误观念的澄清,比如数学意义上的点积叉积并不对应matlab程序中的.*(按位相乘)和*(矩阵乘法)

  2. 内积的物理意义

    • 一种向量到标量的映射
    • 两向量的夹角的计算
    • 两向量是否正交的判断
    • 两向量的相似性(similarity)的度量
  3. 叉积的意义

  4. 如何使用C++语言(STL容器,运算符重载):

    • 表示向量
    • 计算内积
    • 计算叉积
    • 计算模长
    • 计算两向量的夹角
    • 计算点到直线的距离

prerequisites

内积(inner product)

又叫点乘,点积(dot product),数量积,顾名思义得到的是一个标量(scalar)。

  • 代数定义

向量 x=[x1,x2,,xn] y=[y1,y2,,yn] 的内积定义为:

xy=inxiyi=x1y1+x2y2++xnyn

也即:内积等于向量的对应位相乘再相加,如果从函数的观点来看的话,即是两个矢量相互作用得到一个标量。

内积对相互作用的两个向量 x,y 的长度也即各自所含元素的个数是没有限制的。这点不同于向量叉积,叉积所要的向量长度最高为3.

  • 几何定义

欧式空间中,向量是一个同时拥有长度和方向的几何对象。向量 x 的长度记为 x ,两个向量的内积定义为:

xy=xycosθ

θ 标识着两向量的夹角。可见当向量正交时, θ=90 xy=0
当向量 y 被归一化为长度为1的单位向量时, xy=xcosθ ,我们来考察 x,y 均为二维的情形:

《点乘和叉乘及其物理意义(C++STL实现)》

如上图所示,此时二者的内积表示的恰是其中一个向另外一个的投影,投影长度越小,说明二者的夹角越大,反之亦然。当两向量同时归一化为1时,此时内积的定义为: xy=cosθ ,内积越大,说明两者夹角越小,也间接地说明两者也就越相似,故在许多机器学习的算法,常用余弦相似性来度量两特征向量的逼近程度。内积既然能够表征两向量的夹角,自然判断两向量是否正交(值为0)就更不在话下了。

叉积

又叫叉乘(cross product)或者外积,它的计算结果是一个向量而非标量。叉积所在的向量与参与运算的两个向量都正交,也即正交于原来的两个向两边所决定的平面,也即两向量所决定的平面的法向量可通过计算叉积的方式得以确定。当参与运算的两向量是平行的两个向量时,得到的叉积为0,也即可通过计算叉积的方式判断两向量是否平行。

代数定义

  • 二维时

x×y=x1y2x2y1

  • 三维时

《点乘和叉乘及其物理意义(C++STL实现)》

根据如图的计算方法可得:

u×v====iu1v1ju2v2ku3v3(u2v3i+u3v1j+u1v2k)(u3v2i+u1v3j+u2v1k)(u2v3u3v2)i+(u3v1u1v3)j+(u1v2u2v1)k(u2v3u3v2,u3v1u1v3,u1v2u2v1)

几何定义

x×y=xysinθn⃗ 

n⃗  表示叉积方向上的单位向量。

中学知识告诉我们三角形的面积计算公式为:

S=xysinθ2=zh2

其中

θ 表示的是

x,y 之间的夹角,由以上两个公式我们可得到
三角形的高或者
点到其所对的边的距离,也即点到直线的距离,的计算公式:


h=x×y/z

C++ STL实现

  • 向量的定义:
typedef vector<double> Vec;
  • 矩阵减法
Vec operator-(const Vec& x, const Vec& y)
{
    assert(x.size() == y.size());
                        // #include <cassert>
    Vec tmp;
    for(size_t i = 0; i < x.size(); ++i)
        tmp.push_back(x[i] - y[i]);
    return tmp;         // 返回局部变量的拷贝
}
  • 内积和叉积的定义

为了形式的简单,我们在C++中以*表示内积,以^表示叉积,分别对二者进行运算符重载:

double operator*(const Vec& x, const Vec& y)
{
    assert(x.size() == y.size());       // #include <cassert>
    double sum = 0.;
    for (size_t i = 0; i < x.size(); ++i)
        sum += x[i]*y[i];
    return sum;
}

// 三维的情况
Vec operator^(const Vec& x, const Vec& y)
{
    assert(x.size() == y.size() && x.size() == 3);
    return Vec{x[1]*y[2]-x[2]*y[1], 
                x[2]*y[0]-x[0]*y[2],
                x[0]*y[1]-x[1]*y[0]};
            // uniform initialization, C++11新特性
}

// 二维就姑且返回其模长吧
double twoDCrossProd(const Vec& x, const Vec& y)
{
    return x[0]*y[1]-x[1]*y[0];
}
  • 模长或者范数的计算
double norm(const Vec& x)
{
    double val = 0.;
    for(auto elem: x)
        val += elem*elem;
    return sqrt(val);   
                    // #include <cmath>
}
  • 向量夹角的计算
#define PI 3.14159265358979323846
// 弧长向弧度的转换
double toDegree(double val)
{
    return val*180/PI;
}

double angle(const Vec& x, const Vec& y)
{
    return toDegree(acos(x*y/norm(x)/norm(y)));
            // x*y, 计算二者的内积
}
  • 点到直线的距离
// x0, x1, x2 分别表示三角形的三个顶点的坐标
// 这里计算的是点x0到x1和x2构成的直线的距离
double distance(const Vec& x0, const Vec& x1, const Vec& x2)
{
    return twoDCrossProd(x1-x0, x2-x0)/norm(x1-x2);
}

客户端程序:

int main(int, char**)
{
    Vec x{
  1, 0, 0}, y{
  0, 1, 0};
    Vec z = x^y;        // 计算叉乘
    copy(z.begin(), z.end(), ostream_iterator<double>(cout, " "));
    cout << endl;
        // 0 0 1

    Vec alpha{
  1, 0}, beta{
  1, 1};
    cout << angle(alpha, beta) << endl;
            // 45

    Vec x0{
  0, 0}, x1{
  1, 0}, x2{
  0, 1};   
    cout << distance(x0, x1, x2) << endl;
            // 1/sqrt(2)
    return 0;
}
    原文作者:五道口纳什
    原文地址: https://blog.csdn.net/lanchunhui/article/details/49838669
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞