我需要在Python中确定两个n维向量之间的角度。例如,输入可能是两个列表,如下所示:[1,2,3,4]
和[6,7,8,9]
。
使用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
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}')
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)
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的解决方案快大约三倍。
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(...)
不是)。
可以预先归一化向量u
和v
,但是除法实际上会损失数值精度。如果我们使用经典的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))