Python中两个n维向量之间的夹角

117

我需要在Python中确定两个n维向量之间的角度。例如,输入可能是两个列表,如下所示:[1,2,3,4][6,7,8,9]


1
这是最好的答案,来自@MK83,因为它恰好是数学表达式theta = atan2(u^v, u.v)。即使在u=[0 0]或v=[0 0]的情况下也有涵盖,因为这是atan2产生NaN的唯一时间,在其他答案中,NaN将由/norm(u)或/norm(v)产生。 - PilouPili
15个回答

2

使用numpy中的一些函数。

import numpy as np

def dot_product_angle(v1,v2):

    if np.linalg.norm(v1) == 0 or np.linalg.norm(v2) == 0:
        print("Zero magnitude vector!")
    else:
        vector_dot_product = np.dot(v1,v2)
        arccos = np.arccos(vector_dot_product / (np.linalg.norm(v1) * np.linalg.norm(v2)))
        angle = np.degrees(arccos)
        return angle
    return 0

0
import math

ax, ay = input('输入向量a的x和y坐标:').split()

ax, ay = float(ax), float(ay)

bx, by = input('输入向量b的x和y坐标:').split()

bx, by = float(bx), float(by)

ab = ax * bx + ay * by

a = math.sqrt(ax * ax + ay * ay)

b = math.sqrt(bx * bx + by * by)

cos = ab / (a*b)

rad = math.acos(cos)

deg = math.degrees(rad)

print (f'θ = {deg}')


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0
使用numpy并注意BandGap的舍入误差:
from numpy.linalg import norm
from numpy import dot
import math

def angle_between(a,b):
  arccosInput = dot(a,b)/norm(a)/norm(b)
  arccosInput = 1.0 if arccosInput > 1.0 else arccosInput
  arccosInput = -1.0 if arccosInput < -1.0 else arccosInput
  return math.acos(arccosInput)

请注意,如果其中一个向量的大小为零(除以0),此函数将抛出异常。

0

OP提到了n维度的问题,其中n>2,但很多人可能会在这里遇到二维问题,所以我想阐明那种特殊情况下的最佳解决方案。

所有提出的解决方案都使用了反余弦函数(除了MK83和faken),因此对于接近0或180度的角度,都非常不准确且容易出错。这是因为角度的极大变化几乎不会在这些值的余弦中引起任何变化。

arccos的另一个问题(针对二维情况)是它无法区分正角和负角。因此,(0,1)和(1,0)之间的角度将与(1,0)和(0,1)之间的角度相同,尽管第一个角度应该是90度,而第二个角度是-90度。

faken对于OP的多维问题有一个优秀的回答,避免了使用arccos,因此在整个范围内都是准确的。

MK83使用atan2解决了二维问题,这是专门为这个问题提供的函数。答案的范围是-180度到180度。我在这里提出一个仅适用于二维的解决方案,它比MK83更简单和更快。

def angle(a, b, c=None):
    """ This function computes angle between vector A and vector B when C is None
        and the angle between AC and CB, when C is a vector as well.
        All vectors must be two dimensional.
    """
    if c is None:
        angles = np.arctan2([a[1], b[1]], [a[0], b[0]]])
    else:
        angles = np.arctan2([a[1]-c[1], b[1]-c[1]], [a[0]-c[0], b[0]-c[0]])
    return np.degrees(angles[1] - angles[0])

这比MK83的解决方案快大约三倍。


0
给定两个向量vec(u)vec(v),可以证明如下:
2 * atan ( norm(norm(u) * v - norm(v) * u)/norm(norm(u) * v + norm(v) * u) )

这是最稳定的数值解决方案。相比之下,该公式具有以下优点:

acos(dot_product(u,v)/(norm(u)*norm(v)))
asin(norm(cross_product(u,v))/(norm(u)*norm(v)))

atan公式在小角度上是数值稳定的(其中acos(...)对于小角度不如cos(th) ~ 1 - 0.5*th^2),并且在接近Pi/2的角度上也是数值稳定的(由于不稳定的叉积计算,asin(...)不是)。

可以预先归一化向量uv,但是除法实际上会损失数值精度。如果我们使用经典的atan2函数,我们将得到最佳的数值稳定解。

因此,使用numpy,实现非常简单:

_nu = numpy.linalg.norm(u)
_vu = numpy.linalg.norm(v)
numpy.arctan2( numpy.linalg.norm( u * _nv - v * _nu),
               numpy.linalg.norm( u * _nv + v * _nu))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接