3.3 点积:测量向量对齐
我们已经见过的一种向量乘法是标量乘法,将一个标量(实数)和一个向量结合起来,得到一个新的向量。不过还没有讨论过任何将向量相乘的方法。实际上,有两种重要的方法可以做到这一点,二者都提供了重要的几何学见解。一种叫作点积,使用点运算符书写(例如,);另一种叫作向量积(例如,)。对于数来说,这些符号的意思是一样的,如3·4 = 3 × 4。对于两个向量来说,运算和不仅仅有不同的符号,而且代表的意义完全不同。
点积取两个向量并返回一个标量(数),而向量积取两个向量并返回另一个向量。然而,使用这两种运算都可以推断出三维空间中向量的长度和方向。我们首先从点积开始介绍。
3.3.1 绘制点积
点积(也叫内积)是对两个向量的运算,返回一个标量。换句话说,给定两个向量和,那么的结果是实数。点积适用于二维、三维等任意维度的向量。它可以被看作测量输入向量对的“对齐程度”。首先来看看平面上的一些向量,以及它们的点积,以便对这个运算有一些直观的认识。
向量和的长度分别为4和5,而且方向几乎相同。它们的点积为正,意味着它们是对齐的(见图3-24)。
图3-24 大致对齐的两个向量给出一个大的正点积
指向相似方向的两个向量的点积为正,并且向量越大,乘积就越大。对于同样对齐的较短向量,点积较小但仍然是正的。新向量和的长度都是2(见图3-25)。
图3-25 指向相似方向的两个较短向量,点积较小但仍为正
相反,如果两个向量指向相反或大致相反的方向,则其点积为负(见图3-26和图3-27)。向量越长,则点积的负值越小。
图3-26 指向相反方向的向量点积为负
图3-27 指向相反方向的较短向量,点积较大但仍为负数
并非所有的向量对都明确地指向相似或相反的方向,点积可以检测这一点。如图3-28所示,如果两个向量的方向完全垂直,那么无论它们的长度如何,点积都是零。
图3-28 垂直向量的点积总是为零
这就是点积最重要的应用之一:在不做任何三角运算的情况下,计算两个向量是否垂直。这种垂直的情况也可以用来区分其他情况:如果两个向量的夹角小于90°,则向量的点积为正;如果夹角大于90°,则向量的点积为负。虽然还没有讲到计算点积的方法,但你现在知道如何解释这个值了。接下来介绍如何计算它。
3.3.2 计算点积
给定两个向量的坐标,有一个计算点积的简单公式:将相应的坐标相乘,然后将乘积相加。例如,在点积 (1, 2, -1)·(3, 0, 3) 中,坐标的乘积为3,坐标的乘积为0,坐标的乘积为-3,因为相加为 3 + 0 + (-3) = 0,所以点积为零。如果我说得没错,这两个向量应该是垂直的。如果绘制它们并从正确的角度去看,就能证明这一点(见图3-29)。
图3-29 点积为零的两个向量在三维空间中确实是垂直的
在三维空间中,我们的视角可能有误导性,这使得计算出向量的相对方向比目测更有价值。再看一个示例,图3-30显示了二维向量(2, 3)和(4, 5)在平面上具有相似的方向。坐标的乘积是2·4 = 8,而坐标的乘积是3·5 = 15。8 + 15 = 23就是点积的结果。这个结果是一个正数,证实了向量的夹角小于90°。它们在三维空间中可以表示为恰好位于平面内的向量(2, 3, 0)和(4, 5, 0)。但是无论在二维平面还是三维空间中,它们的相对几何性质是不变的。
图3-30 计算点积的另一个示例
在Python中,可以实现一个点积函数来处理任意一对(只要它们的坐标数目相同即可)输入向量。例如:
def dot(u,v):
return sum([coord1 * coord2 for coord1,coord2 in zip(u,v)])
这段代码使用Python的zip
函数对相应的坐标进行配对,然后在推导式中将每对坐标相乘,并添加到结果列表中。下面就借助它,进一步探索点积的行为。
3.3.3 点积的示例
位于不同轴上的两个向量的点积为零并不奇怪。这说明它们是垂直的。
>>> dot((1,0),(0,2))
0
>>> dot((0,3,0),(0,0,-5))
0
我们还可以证实,向量越长,其点积的绝对值越大。例如,将任意一个输入向量乘以2,点积的输出就会翻倍。
>>> dot((3,4),(2,3))
18
>>> dot(scale(2,(3,4)),(2,3))
36
>>> dot((3,4),scale(2,(2,3)))
36
这说明,点积的绝对值与其输入向量的长度成正比。如果取同方向两个向量的点积,那么点积就等于两个向量长度的乘积。例如,(4, 3)的长度为5,(8, 6)的长度为10,所以二者的点积等于5·10。
>>> dot((4,3),(8,6))
50
当然,点积并不总是等于其输入向量长度的乘积。如图3-31所示,向量(5, 0)、(-3, 4)、(0, -5)和(-4, -3)的长度都是5,但它们与原始向量(4, 3)的点积是不同的。
图3-31 由于方向不同,相同长度的向量与向量 (4, 3)有不同的点积
两个长度为5的向量的点积范围是-25~25:当它们指向相反方向时,点积为-25;当它们对齐时,点积为5·5 = 25。在3.3.5节的练习中你会发现,两个向量的点积范围是长度乘积到长度乘积的负值。
3.3.4 用点积测量角度
我们已经知道,点积是根据两个向量的夹角而变化的。具体来说,当夹角角度为0到180°时,点积的取值范围是和长度乘积的1到-1倍。我们已经见过具有这样特征的函数,即余弦函数。其实点积还有另一个公式。如果和分别表示向量和的长度,那么点积的计算公式为:
是向量和之间的角度。原则上,这提供了一种计算点积的新方法。通过测量两个向量的长度和它们之间的角度,就可以得到点积的结果。如图3-32所示,假设已知有两个长度分别为3和2的向量,并使用量角器测量出它们的夹角是75°。
图3-32 长度分别为3和2的两个向量,夹角为75°
图3-32中两个向量的点积为。通过适当的弧度转换,我们可以使用Python计算出这个值约为1.55。
>>> from math import cos,pi
>>> 3 * 2 * cos(75 * pi / 180)
1.5529142706151244
在使用向量进行计算时,更常见的是基于坐标来计算角度。我们可以结合这两个公式来算出一个角:首先使用坐标计算向量的点积和长度,然后求解角度。
让我们找出向量(3, 4)和(4, 3)之间的角度。它们的点积是24,向量长度都是5。从新的点积公式可以得出:
可以将简化成。使用Python的math.acos
库,可以得到值约为0.284弧度或16.3°,其余弦值为24/25。
这个练习提醒了我们,为什么在二维平面上不需要点积。第2章展示了如何得到向量与轴正方向之间的角度。创造性地使用那个公式,可以在平面上找到我们想要的任意角度。点积在三维空间中才真正开始发挥作用,因为三维空间中的坐标变换对测量角度的帮助并不大。
例如,我们可以用同样的公式来求(1, 2, 2)和(2, 2, 1)之间的角度。它们的点积是1·2 + 2·2 + 2·1 = 8,向量长度都是3。这意味着,所以,约为0.476弧度或27.3°。
这个过程在二维平面或三维空间中是一样的,将被反复使用。通过实现Python函数来求两个向量之间的角度可以节省一些精力。因为dot
函数和length
函数中都没有硬编码维数,所以这个新函数也不会。利用这个公式可得:
以及
第二个公式可以被直接翻译成如下Python代码。
def angle_between(v1,v2):
return acos(
dot(v1,v2) /
(length(v1) * length(v2))
)
这段Python代码没有依赖向量和的维数。它们既可以是包含2个坐标的元组,也可以是包含3个坐标的元组(实际上,还可以是包含4个或更多坐标的元组,我们将在后面的章节中讨论)。相比之下,接下来要说的向量积(外积、叉积)只在三维空间中有效。
3.3.5 练习
练习3.11:根据图3-33,将、和从大到小排列。
图 3-33
解:乘积是唯一正的点积,因为和是唯一一对夹角小于直角的向量对。此外,比更小(更负),因为既大又离更远,所以。
练习3.12:(-1, -1, 1)和(1, 2, 1) 的点积是多少?这两个三维向量的夹角是大于90°、小于 90°,还是正好等于90°?
解:(-1, -1, 1)和(1, 2, 1)的点积为-1·1 + -1·2 + 1·1 = -2。因为结果是负数,所以两个向量之间的角度超过90°。
练习3.13(小项目):对于两个三维向量和,和的值都等于。在这种情况下,,而和都是36,是原结果的2倍。请证明这个规则对于任意实数都适用,而不仅仅是2。换句话说,请证明对于任意,和的值都等于。
解:设和的坐标为和,那么。因为,,我们可以通过展开点积来计算。
上式证明了标量乘法会对点积的结果进行相应的缩放处理。
另一个点积同理,以下公式证明了同样的事实。
练习3.14(小项目):用代数证明向量与其自身的点积是其长度的平方。
解:如果一个向量的坐标是,那么它与自身的点积是,确实是其长度的平方。
练习3.15(小项目):找出长度为3的向量和长度为7的向量,使。再找出一对向量和,使。最后,再找出三对长度分别为3和7的向量,并证明它们的长度都在-21和21之间。
解:两个方向相同的向量(例如,沿轴正方向)具有最高的点积。
>>> dot((3,0),(7,0)) 21
两个方向相反的向量(例如,分别沿轴正负方向)具有最低的点积。
>>> dot((0,3),(0,-7)) -21
利用极坐标,可以很容易地再生成一些长度为3和7的任意角度的向量。
from vectors import to_cartesian from random import random from math import pi def random_vector_of_length(l): return to_cartesian((l, 2 *pi*random())) pairs = [(random_vector_of_length(3), random_vector_of_length(7)) for i in range(0,3)] for u,v in pairs: print("u = %s, v = %s" % (u,v)) print("length of u: %f, length of v: %f, dot product :%f" % (length(u), length(v), dot(u,v)))
练习3.16:设和是向量,其中,。如果和的夹角是101.3°,那么是什么?
(a) 5.198
(b) 5.098
(c) -1.019
(d) 1.019
解:同样可以将这些值代入新的点积公式,并通过适当的弧度转换,使用Python计算结果。
>>> 3.61 * 1.44 * cos(101.3 * pi / 180) -1.0186064362303022
四舍五入到小数点后三位,答案与(c)一致。
练习3.17(小项目):通过把(3, 4)和(4, 3)转换为极坐标并取角的差值,来求出它们之间的角度。答案是以下哪一个?
(a) 1.569
(b) 0.927
(c) 0.643
(d) 0.284
提示:结果应与点积公式求得的值一致。
解:因为从轴正半轴开始沿逆时针方向看,向量(3, 4)比(4, 3)距离更远,所以用(3, 4)的角度减去(4, 3)的角度就能得到答案。结果与答案(d)完全吻合。
>>> from vectors import to_polar >>> r1,t1 = to_polar((4,3)) >>> r2,t2 = to_polar((3,4)) >>> t1-t2 -0.2837941092083278 >>> t2-t1 0.2837941092083278
练习3.18:(1, 1, 1)与(-1, -1, 1)之间的角是多少度?
(a) 180°
(b) 120°
(c) 109.5°
(d) 90°
解:两个向量的长度都是,约等于1.732。它们的点积是1·(-1) + 1·(-1) + 1·= -1,即。所以,。由此可求得这个角约为1.911弧度或109.5°(答案是(c))。