【回归本源】1.3-向量叉乘
好的,今天麦子继续跟大家来讲讲两个向量的另一种乘法--叉乘
注:同上篇一样,部分地方会用到矩阵,我们还没讨论过,我会在有矩阵的段落开头加上(*)做为标记,对矩阵不熟的朋友可以之后返回来看,但是不会也并不影响阅读。
叉乘(Cross Product)
叉乘的结果就是一个向量,长这样
v→×u→=(
vyuz−vzuy |
vzux−vxuz |
vxuy−vyux |
)
,
它的几何含义也非同寻常,
(v→×u→)
代表着同时垂直于
v→
和
u→
的一个向量,换言之,
(v→×u→)
是垂直于向量
v→
和
u→
所在平面的一个向量。这也是我们如何计算一个平面法向量,
n→
,的方法。而这个向量的大小,与点乘对应,可以被写成
|v→×u→|=|v→||u→|sin(θ)
,
<img src="https://pic1.zhimg.com/v2-0eb49a76ad9c75462f77543898679960_b.jpg" data-rawwidth="1484" data-rawheight="1000" data-size="normal" data-caption="" class="origin_image zh-lightbox-thumb" width="1484" data-original="https://pic1.zhimg.com/v2-0eb49a76ad9c75462f77543898679960_r.jpg"/>
至于两种表达式数值相等的原因,麦子在这里简单写一写,
|v→×u→|
2
=(vyuz−vzuy)
2
+(vzux−vxuz)
2
+(vxuy−vyux)
2
=(vy
2
+vz
2
)ux
2
+(vx
2
+vz
2
)uy
2
+(vx
2
+vy
2
)uz
2
−
2
vxuxvyuy−
2
vxuxvzuz−
2
vyuyvzuz
,
然后为了进一步简化公式,我们可以加上再减去
(vx
2
ux
2
+vy
2
uy
2
+vz
2
uz
2
)
,
⇒|v→×u→|
2
=(vx
2
+vy
2
+vz
2
)(ux
2
+uy
2
+uz
2
)−(vxux+vyuy+vzuz)
2
⇒|v→×u→|
2
=|v→|
2
|u→|
2
−(v→⋅u→)
2
没错,点乘也加入了其中,需要复习下的可以回看下上一篇
而我们离最后的结论也之差临门一脚了:
cos
2
(θ)+sin
2
(θ)=
1
,
⇒|v→×u→|
2
=|v→|
2
|u→|
2
−|v→|
2
|u→|
2
cos
2
(θ)=|v→|
2
|u→|
2
(
1
−cos
2
(θ))
,
因此我们就有了这两个式子的数值关系了
|v→×u→|=|v→||u→|sin(θ)
,
希望你能留意到,叉乘的这个推导里3D是必须条件。
在上一讲我们也提到过
点乘的大小也反映了两个向量有多“平行”
与之对应的,叉乘的大小反映了两个向量有多“垂直”。
而上一讲中我们举过一个例子,是力与位移的乘积是做功,我们强调过,这实际上是个点乘。而叉乘的概念实际上初中的大家也接触过,除了做功,还有另一个物理量也是力和距离的乘积,那就是力矩(Moment)。相信你也猜到了,力矩就是一个叉乘概念。事实上,阿基米德意识到力矩的概念的时候还没有叉乘的概念,因此在叉乘出来后便有了另一个物理量来泛化力矩的概念,那就是转矩(Torque),
τ→
。
距离支点一定距离
r→
,垂直方向的力给物体提供了一个转矩:
τ→=r→×F→;
|τ→|=(Fsin(θ))r
<img src="https://pic1.zhimg.com/v2-f65435ef7aed612a282975f6c8c6ffa4_b.jpg" data-rawwidth="1419" data-rawheight="1000" data-size="normal" class="origin_image zh-lightbox-thumb" width="1419" data-original="https://pic1.zhimg.com/v2-f65435ef7aed612a282975f6c8c6ffa4_r.jpg"/>
假设图中为一密度不均椭圆,pivot为支点固定在某个位置,Xcom为物体质心,向量r从支点指向质心,向量Fg为重力垂直与地面,转矩求法为上面的公式
以上,就是关于叉乘的一些基本介绍。但是如果你细细地品,不难发现麦子我埋了两个坑:
如果叉乘是同时垂直与两个向量的一个向量,它是往哪个方向的呢?
我们关于叉乘数值的推导是建立在麦子一开始写的式子是正确的前提下的,那么为什么叉乘是那样表示的呢?
首先是第一个问题。
叉乘的方向是怎么确定的?
这里存在约定俗称的部分,我们一般使用的是右手参照系。这种定义方法并不是一定的,比如在Unity中就是采用左手参照系。
判断叉乘方向的方法也有很多种,每种都是等效的,但是基本上习惯了一种就不会再去学别的方法了。麦子这里只提一下自己的判断方法:右手螺线定则。(在右手参照系中)
<img src="https://pic3.zhimg.com/v2-800b4473d22e393aae359d5f1bfbab96_b.jpg" data-rawwidth="216" data-rawheight="233" data-size="normal" class="content_image" width="216"/>
将右手四个手指指向u的方向,朝着v的方向弯曲,那么大拇指指向的方向就是(u x v)的方向
之所以大部分时候用右手参照系应该有两点原因(麦子的个人推测啊,有大神觉得我说的不正确并知道真实原因,欢迎留言指正一下),在数学中有两个大家都认的约定:
在2D的情况下,角度一般是按逆时针方向定义的
对于一个封闭图形而言,法线正方向指向外边
<img src="https://pic3.zhimg.com/v2-655130485db5a1f7af14f444263b2b22_b.jpg" data-rawwidth="1601" data-rawheight="1000" data-size="normal" class="origin_image zh-lightbox-thumb" width="1601" data-original="https://pic3.zhimg.com/v2-655130485db5a1f7af14f444263b2b22_r.jpg"/>
左图是一个2D平面,角度是从x轴的正数方向出发,逆时针增加;右图为一个封闭球体,法线方向是以圆心为中心,射线向外的
<img src="https://pic1.zhimg.com/v2-f6dab1478236796695e27e1dc85d0474_b.jpg" data-rawwidth="1178" data-rawheight="1000" data-size="normal" class="origin_image zh-lightbox-thumb" width="1178" data-original="https://pic1.zhimg.com/v2-f6dab1478236796695e27e1dc85d0474_r.jpg"/>
图为一个开放曲面,v和u为曲面上某一点的两个不共线向量,两者所在平面为灰色的,应该为曲面那一点的一个切面,而(v x u)应该按右手螺旋定则确定方向
因此在这个封闭图形的表面上,随意取两个不共线的向量,那么要同时满足上面两个条件的话,必须默认右手参照系。为了保证一致性,对于一个开放图形而言,也应该符合右手参照系。(至于为什么Unity一般默认为左手参照系,那就是另一个故事了,这个坑我们之后也会填上)
至于第二个问题,麦子决定放在文末。因为涉及大量数学推导,不感兴趣的朋友不看也罢。就请先相信麦子没坑你们吧。
这里我们来列举下叉乘所带来的向量性质吧。
v→×u→=−(u→×v→)
(av→)×u→=a(v→×u→)
v→×(u→+w→)=v→×u→+v→×w→
v→×v→=
0
→=(
) (v→×u→)⋅w→=(w→×v→)⋅u→=(u→×w→)⋅v→ v→×(u→×v→)=v→×u→×v→=v→ 2 u→−(v→⋅u→)v→ |
同样的可以去证一证这些性质,其中
(v→×u→)⋅w→
具有很重要的几何性质,我们会在下一讲,向量的最后一讲里提及到。在最后一个公式里
v→
2
=(v→⋅v→)
。
v→×v→=
0
→
则反映了当两个向量共线时,并不存在一个唯一的垂直向量。同时,另一种看法是:两个向量所在平面上,由这两个向量所为围成的平行四边形面积为0。因为
|v→×u→|=|v→||u→|sin(θ)
代表一个平行四边形的面积(图一中的灰色面积)。
回顾下第一篇文章中我们提到过单位向量,这个向量代表着一个特定的方向,且长度为1。
而我们也频繁得会用
(
vx |
vy |
vz |
)
来代表一个向量,那么不难意识到,有一组单位向量十分特殊:
i^=(
1 |
0 |
0 |
);j^=(
0 |
1 |
0 |
);k^=(
0 |
0 |
1 |
)
,
这三个单位向量分别指向了
x
,
y
和
z
方向。而且
i^×j^=k^;j^×k^=i^;k^×i^=j^
,
这组关系你也可以用最上面麦子写的那个公式验证一下。由此可见,叉乘可以用来确定一个笛卡尔坐标系(Cartesian coordinate)。
(*) 叉乘的公式可谓是又丑又长,肯定有小伙伴可以记住,但是麦子我是真记不住。这里就要说下叉乘的一种记忆方法了,这里需要借用下矩阵中行列式(Determinant)。
v→×u→=|
i^ |
j^ |
k^ |
vx |
vy |
vz |
ux |
uy |
uz |
|
,
同时这里简单介绍下这种行列式的求解方法。
<img src="https://pic4.zhimg.com/v2-ab30485e733ed64fabf2bdb5b2ef033f_b.jpg" data-rawwidth="1620" data-rawheight="1000" data-size="normal" class="origin_image zh-lightbox-thumb" width="1620" data-original="https://pic4.zhimg.com/v2-ab30485e733ed64fabf2bdb5b2ef033f_r.jpg"/>
红色线路为相加;蓝色线路为相减
因此
v→×u→=(vyuz−vzuy)i^+(vzux−vxuz)j^+(vxuy−vyux)k^=(
vyuz−vzuy |
vzux−vxuz |
vxuy−vyux |
)
,
注意下,这种做法并非行列式的泛用解法,对于4D以及以上多维的行列式均不适用,但是对3D是没问题的,而且麦子觉得还容易,所以我常用。(对于行列式以及泛用解法我们会在之后的章节中提起)
(*) 在计算机中,我们还常用另一个矩阵来一步到位地去求叉乘结果
v→×u→=Vu=[
0 |
−vz |
vy |
vz |
0 |
−vx |
−vy |
vx |
0 |
][
ux |
uy |
uz |
]
。
好的,老规矩,补齐下之前的那个Vector Class
class Vector_3d(object):
def __init__(self, x, y, z, ifTransposed=False):
# 默认是竖向量
super(Vector_3d, self).__init__()
self.x = x
self.y = y
self.z = z
self.ifTransposed = ifTransposed
# ......
def cross(self, another_vector):
assert type(another_vector) is Vector_3d
return Vector_3d(
self.y*another_vector.z - self.z*another_vector.y,
self.z*another_vector.x - self.x*another_vector.z,
self.x*another_vector.y - self.y*another_vector.x
)
def triangle_area(p1, p2, p3):
# 给三个坐标点(x, y, z),利用叉乘去判断三角形的面积大小
# 希望你能自己想明白为什么是这样的。这个function默认所有p都是Vector_3d
v1 = p2 - p1
v2 = p3 - p1
return 1/2 * abs(v1.cross(v2))
越往后我们这组代码将会越来越难调试(尤其是加上了矩阵的乘法后),麦子会继续尝试用这种逻辑方法写下去。但是为了更直接、快捷地使用,会在介绍完矩阵后更改为用Numpy的实现方式,之后尝试用我们写的Vector和Matrix去做一些图像学的操作,如光线追踪。一再强调,现实中别用Python搞这个,太慢了。
想深入了解下Numpy的小伙伴可以看下这个
图解NumPy:常用函数的内在机制191 赞同 · 6 评论文章
想用Python学图像的小伙伴可以了解下Taichi,不多解释,去看了就懂我为啥这么说了。
好了,叉乘的介绍就到这里了,下一篇就是给向量收个尾了,会讲一些关于直线、平面等的定义,再之后就是矩阵了,有兴趣的小伙伴记得点个收藏哦!
好的,这里就是一开始那个叉乘向量怎么来的过程了。这里我会写两个证明:第一个需要用到
i^
,
j^
和
k^
以及叉乘的性质。第二个则是直接从叉乘的几何性质来的。第一个会更简单,但是逻辑上是不通的,因为你需要用叉乘的公式去证明叉乘的性质,因此不应该用其性质证明它的公式。第二个则会更泛化,因为它并没有一开始对坐标系进行任何限制以及不会用到任何你学过的叉乘性质。那,让我们开始吧!
第一种
v→×u→=(vxi^+vyj^+vzk^)×(uxi^+uyj^+uzk^)
=vxux(i^×i^)+vxuy(i^×j^)+vxuz(i^×k^)+
vyux(j^×i^)+vyuy(j^×j^)+vyuz(j^×k^)+
vzux(k^×i^)+vzuy(k^×j^)+vzuz(k^×k^)
其中 因为共线,所以类似
(i^×i^)=
0
→
就排除了3项。而叉乘交换位置又会导致符号变化,例如
(i^×j^)=−(j^×i^)
。三个单位向量还互相垂直。因此
v→×u→=(vyuz−vzuy)i^+(vzux−vxuz)j^+(vxuy−vyux)k^
,
也就有了我们一开始的公式了。一切看起来都很自然,我相信有很多朋友就是这样推导叉乘的。但是仔细一想就会明白,这个推导的第二步从
(vxi^+vyj^+vzk^)×(uxi^+uyj^+uzk^)
到下面用了乘法分配律,但是没有叉乘公式,怎么证明叉乘符合乘法分配律呢?因此,不可取。
第二种
那么就让我们从叉乘的几何意义着手吧。深吸一口气,我们开始吧!
回忆一下,叉乘的几何含义是寻找同时垂直与两个向量的一个向量。我们在找
(v→×u→)
,假设它的方向是
n^
,我们有
n^=v→×u→|v→×u→|
,
同时,我们还有
v→⋅n^=
0
u→⋅n^=
0
|n^|
2
=
1
这三个条件,点乘为0意味着垂直,单位向量自然长度为1。进而我们得到了一个多项式组
vxnx+vyny+vznz=
0
uxnx+uyny+uznz=
0
nx
2
+ny
2
+nz
2
=
1
相比你也知道了,我们需要解这个多项式,你可以自己尝试下。麦子这里就把我写的过程打在这儿了,从第一个得到
nz=−vxvznx−vyvzny
代入第二个
uxnx+uyny+uz(−vxvznx−vyvzny)=
0
⇒(ux−uzvxvz)nx=(uzvyvz−uy)ny
×vz⇒(vzux−vxuz)nx=(vyuz−vzuy)ny
⇒ny=(vzux−vxuzvyuz−vzuy)nx
再带回
nz
,
nz=−vxvznx−vyvz(vzux−vxuzvyuz−vzuy)nx
=−nxvz(vx+vy(vzux−vxuzvyuz−vzuy))
=−nxvz(vxvyuz−vxvzuy+vyvzux−vxvyuzvyuz−vzuy)
Nice,到了快乐的消项时间了
⇒nz=(vxuy−vyuxvyuz−vzuy)nx
现在,我们可以把
ny
和
nz
都代进第三个式子了
nx
2
(
1
+(vzux−vxuz)
2
(vyuz−vzuy)
2
+(vxuy−vyux)
2
(vyuz−vzuy)
2
)=
1
⇒nx
2
((vyuz−vzuy)
2
+(vzux−vxuz)
2
+(vxuy−vyux)
2
(vyuz−vzuy)
2
)=
1
到这里后,我们需要试着简化这个式子了,令
L
2
=(vyuz−vzuy)
2
+(vzux−vxuz)
2
+(vxuy−vyux)
2
于是
nx
2
=(vyuz−vzuy)
2
L
2
⇒nx=±vyuz−vzuyL
怎么样?看到希望的曙光了吗?再代入我们之前算的
ny
和
nz
ny=±vzux−vxuzL;nz=±vxuy−vyuxL
因此
n^=±
1
L(
vyuz−vzuy |
vzux−vxuz |
vxuy−vyux |
)
而
L
L=(vyuz−vzuy)
2
+(vzux−vxuz)
2
+(vxuy−vyux)
2
就应该是一个长度,是不是恰好更我们一开始写的那个式子对应上了呀!
n^=v→×u→|v→×u→|
,
其中
v→×u→=(
vyuz−vzuy |
vzux−vxuz |
vxuy−vyux |
)
,
|v→×u→|=(vyuz−vzuy)
2
+(vzux−vxuz)
2
+(vxuy−vyux)
2
,
我们便有了我们一开始要求的东西。值得注意的就是那个正负号,说明了这个向量的方向是需要我们定义的,而正如正文里说的,我们的定义不过是一个约定俗成而已。
如果你耐心地自己证明了或过了一遍麦子我的证明过程,希望你对叉乘也更了解了一点,这个叉乘公式并不是随便提出来
from:https://zhuanlan.zhihu.com/p/342679387
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」