Python中两个n维向量之间的角度
我需要在 Python 中确定两个 n 维向量之间的角度。例如,输入可以是如下两个列表:[1,2,3,4]
和 [6,7,8,9]
。
I need to determine the angle(s) between two n-dimensional vectors in Python. For example, the input can be two lists like the following: [1,2,3,4]
and [6,7,8,9]
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
使用 numpy 中的一些函数。
Use some functions from numpy.
使用 numpy 并处理 BandGap 的舍入误差:
请注意,如果其中一个向量的大小为零(除以 0),此函数将引发异常。
Using numpy and taking care of BandGap's rounding errors:
Note, this function will throw an exception if one of the vectors has zero magnitude (divide by 0).
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}')
ax, ay = input('Enter x and y of vector a: ').split()
ax, ay = float(ax), float(ay)
bx, by = input('Enter x and y of vector b: ').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}')
OP 要求 n 维 n>2,但很多人最终都会遇到二维问题,所以我想澄清这种特殊情况的最佳解决方案。
所提出的解决方案都使用反余弦函数(MK83 和 faken 除外),因此都非常不准确,并且对于接近 0 或 180 度的角度容易出错。这是因为角度的巨大变化导致这些值下角度的余弦几乎没有变化。
arccos(对于二维情况)的第二个问题是它无法区分正角和负角。因此,(0,1) 和 (1,0) 之间的角度将与 (1,0) 和 (0,1) 之间的角度相同,尽管第一个角度应为 90 度,第二个角度应为 -90 度。
faken 对 OP 多维问题有一个很好的答案,避免使用 arccos,因此在整个范围内都是准确的。
MK83 使用
atan2
解决二维问题,atan2 是为此问题提供的函数。答案范围是 -180 度到 180 度。我这里提出一个只针对二维的解决方案,比MK83更简单、更快。这比MK83的解决方案快大约三倍。
The OP was asking for n-dimensions n>2, but many people will end up here for a two dimensional problem, so I want to clarify the best solution for that special case.
The proposed solutions all use the arccosine function (other than MK83 and faken) and so, are all very inaccurate and prone to error for angles near 0 or 180 degrees. That is because very large changes in angle cause almost no change in the cosine of the angle at those values.
A second problem with
arccos
(for the two dimensional case) is that it can not distinguish between a positive angle and a negative angle. So the angle between (0,1) and (1,0) will be the same as that between (1,0) and (0,1) although the first angle should be 90 and the second -90 degrees.faken has an excellent answer to to OPs multidimensional problem that avoids the use of
arccos
, and so is accurate over the whole range.MK83 solves the 2 dimensional problem using
atan2
which is a function that is provided for this exact problem. The answers range from -180 degrees to 180 degrees. I propose a solution here only for two dimensions, which is simpler and faster than MK83This is about three times faster than MK83's solution.
给定两个向量
vec(u)
和vec(v)
,则可以证明以下公式是数值上最稳定的解:该公式相对于任何向量的优点of:
are:
atan
对于小角度来说数值稳定。对于小角度,acos
函数不像cos(th) ~ 1 - 0.5*th^2
那样,atan
在数值上是稳定的/2。当公式计算半角时,它对应于使用叉积的公式中的角度 Pi。在这些条件下,叉积计算不稳定,因此使用asin
的公式也不稳定。人们可以对向量
u
和v
进行预归一化并使用以下形式:然而,除法实际上会损失数值精度。在这种情况下,这种精度损失将出现在标准化和最终除法中。
因此,在实现方面,我们应该使用经典的 atan2 函数来避免再次除法。因此,我们获得了最佳的数值稳定解。
因此,使用 numpy,实现很简单:
Given two vectors
vec(u)
andvec(v)
, then it can be shown that the following formul is the most numerically stable solution:The benefits of this formula over any of:
are:
atan
is numerically stable for small angles. Theacos
function is not ascos(th) ~ 1 - 0.5*th^2
for small anglesatan
is numerically stable for angles around Pi/2. As the formula computes the half-angle, it corresponds to the angle Pi for the formula using the cross-product. Under these conditions, the cross-product computation is unstable and hence then formula usingasin
is unstable.One could pre-normalize the vectors
u
andv
and use the form:however, a division actually looses numeric accuracy. In this case this loss of precision would appear in the normalizations and the final division.
Hence, implementation wise, we should use the classic
atan2
function which avoids another division. Hence, we achieve the best numeric stable solution.So, using numpy, the implementation is straightforward:
这个问题有很多解决方案,但没有一个采用使用
numpy
的简单表达式能够处理跨任何轴的n维数组,如下所示:示例< /strong>
我们在网格上选择点,并在每个点绘制两个向量:一个为黑色,另一个为灰色。然后,我们计算黑色和灰色向量之间的角度,并根据该角度绘制着色的点。
输出:
There are numerous solutions to this question, yet none employ a simple expression using
numpy
capable of handling n-dimensional arrays across any axis, as given by:Example
We select points on a grid and draw two vectors at each point: one in black and the other in grey. Then, we calculate the angle between the black and grey vectors, and plot the points colored according to this angle.
Output:
注意:如果两个向量具有相同的方向(例如,
(1, 0, 0)
、(1, 0, 0)
)或相反方向(例如,(-1, 0, 0)
、(1, 0, 0)
)。这是一个可以正确处理这些情况的函数:
Note: all of the other answers here will fail if the two vectors have either the same direction (ex,
(1, 0, 0)
,(1, 0, 0)
) or opposite directions (ex,(-1, 0, 0)
,(1, 0, 0)
).Here is a function which will correctly handle these cases:
注意:当向量具有相同或相反方向时,此操作将会失败。正确的实现在这里:https://stackoverflow.com/a/13849249/71522
Note: this will fail when the vectors have either the same or the opposite direction. The correct implementation is here: https://stackoverflow.com/a/13849249/71522
使用 numpy (强烈推荐),你会这样做:
Using numpy (highly recommended), you would do:
另一种可能性是仅使用 numpy ,它会为您提供内角
,这是输出:
The other possibility is using just
numpy
and it gives you the interior angleand here is the output:
如果您正在使用 3D 矢量,则可以使用工具带 vg 来简洁地完成此操作。它是 numpy 之上的一个轻层。
您还可以指定视角来通过投影计算角度:
或者通过投影计算带符号的角度:
我在上次启动时创建了该库,它的动机是这样的:在 NumPy 中冗长或不透明的简单想法。
If you're working with 3D vectors, you can do this concisely using the toolbelt vg. It's a light layer on top of numpy.
You can also specify a viewing angle to compute the angle via projection:
Or compute the signed angle via projection:
I created the library at my last startup, where it was motivated by uses like this: simple ideas which are verbose or opaque in NumPy.
求两个向量之间角度的简单方法(适用于n维向量),
Python代码:
Easy way to find angle between two vectors(works for n-dimensional vector),
Python code:
David Wolever 的解决方案很好,但是
如果你想要有符号的角度,你必须确定给定的角度是右手还是左手(参见wiki 了解更多信息)。
我的解决方案是:
由于这个
NotImplementedError
,它并不完美,但对于我的情况来说,它效果很好。这种行为可以修复(因为任何给定的配对都确定了手感),但它需要我想要并且必须编写的更多代码。David Wolever's solution is good, but
If you want to have signed angles you have to determine if a given pair is right or left handed (see wiki for further info).
My solution for this is:
It's not perfect because of this
NotImplementedError
but for my case it works well. This behaviour could be fixed (cause handness is determined for any given pair) but it takes more code that I want and have to write.获取两个向量之间角度的传统方法(即 arccos(dot(u, v) / (norm(u) *norm(v))),如其他答案中所示)存在以下问题几个极端情况下的数值不稳定。以下代码适用于 n 维和所有极端情况(它不检查零长度向量,但这很容易添加,如其他一些答案所示)。请参阅下面的注释。
此代码改编自 Jeffrey Sarnoff 的 Julia 实现(MIT 许可),又基于 < a href="https://people.eecs.berkeley.edu/%7Ewkahan/MathH110/Cross.pdf" rel="noreferrer">这些笔记,作者:W. Kahan 教授(第 15 页)。
The traditional approach to obtaining an angle between two vectors (i.e.
arccos(dot(u, v) / (norm(u) * norm(v)))
, as presented in the other answers) suffers from numerical instability in several corner cases. The following code works for n-dimensions and in all corner cases (it doesn't check for zero length vectors, but that's easy to add, as shown in some of the other answers). See notes below.This code is adapted from a Julia implementation by Jeffrey Sarnoff (MIT license), in turn based on these notes by Prof. W. Kahan (page 15).
对于少数可能(由于 SEO 复杂性)尝试在 python 中计算两条线之间的角度的人,如
(x0, y0), (x1, y1) 几何线,有以下最小解决方案(使用
shapely
模块,但可以轻松修改不):并且使用将是
For the few who may have (due to SEO complications) ended here trying to calculate the angle between two lines in python, as in
(x0, y0), (x1, y1)
geometrical lines, there is the below minimal solution (uses theshapely
module, but can be easily modified not to):And the use would be
以 sgtpepper 的出色答案为基础,添加对对齐向量的支持,并使用 Numba 添加超过 2 倍的加速
%% timeit
结果没有 Numba,,而使用
Building on sgt pepper's great answer and adding support for aligned vectors plus adding a speedup of over 2x using Numba
%%timeit
results without NumbaAnd with