数学域错误的ValueError:来自math.acos和numpy.arccos的NaN。

6

我看到过类似的问题,但还没有找到一个能帮助我的答案。我正在尝试使用点积方法来找到两个向量之间的夹角。

import math as m
import numpy as np

def mag(x):
    return np.sqrt(np.sum(i**2 for i in x))

u = np.array([1,1,1])
v = np.array([-1,-1,-1])

theta = m.degrees(np.arccos(np.dot(u,v) / (mag(u) * mag(v))))

它适用于大多数情况,但当我将u和v设置为相距180度的向量(如上所示)时,会出现ValueError: math domain error。我从m.acos切换到了np.arccos(如上所示),它返回NaN,但本质上是同样的问题。我知道这是由于浮点舍入误差产生一个略低于-1的值,而这个值在acos/arrcos的定义域之外,但我无法想出该怎么办。
print('theta = ', theta)
print('magnitude product = ', mag(u) * mag(v))
print('dot product = ', np.dot(u,v))
print('dot prod / mag prod = ', np.dot(u,v) / (mag(u) * mag(v)))
print('dot prod / mag prod < -1.0 = ', (np.dot(u,v) / (mag(u) * mag(v))) < -1.0)

theta =  nan
magnitude product =  3.0
dot product =  -3
dot prod / mag prod =  -1.0
dot prod / mag prod < -1.0 =  True

我尝试使用十进制模块,但是到目前为止只能让事情变得更糟。我想这不是一个不寻常的问题,所以我猜测应该有一个好的、干净的解决方案,但我就是找不到。

2个回答

12
问题出在浮点数上。对于“np.dot(u,v) / (mag(u) * mag(v))”的结果可能会是类似于“-1.000000000000002”这样的数字,而这不是acos函数所能接受的有效数字(因为cos必须在[-1,1]范围内)。
我建议您使用“np.clip”:
def mag(x):
    return np.sqrt(np.sum(i ** 2 for i in x))

u = np.array([1, 1, 1])
v = np.array([-1, -1, -1])

cos = np.dot(u, v) / (mag(u) * mag(v))
cos = np.clip(cos, -1, 1)
rad = np.arccos(cos)  # or m.acos(cos)
print(rad)
theta = m.degrees(rad)
print(theta)

1
至少移动平方根可以修复您提供的输入,因为在所有中间步骤中结果始终保持为整数。
import numpy as np

def mag2(x):
    return np.dot(x, x)  # or np.sum(x ** 2)

u = np.array([1,1,1])
v = np.array([-1,-1,-1])

theta = np.degrees(np.arccos(np.dot(u,v) / np.sqrt(mag2(u) * mag2(v))))

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