2010-05-13 151 views

回答

41
import math 

def dotproduct(v1, v2): 
    return sum((a*b) for a, b in zip(v1, v2)) 

def length(v): 
    return math.sqrt(dotproduct(v, v)) 

def angle(v1, v2): 
    return math.acos(dotproduct(v1, v2)/(length(v1) * length(v2))) 

注意:當向量具有相同或相反的方向,這將失敗。正確的實現是在這裏:https://stackoverflow.com/a/13849249/71522

+2

另外,如果你只需要cos,sin,tan的角度,而不是角度本身,那麼你可以跳過math.acos得到餘弦,並使用交叉乘積得到正弦。 – mbeckish 2010-05-13 14:17:28

+0

這正是我正在尋找的,謝謝! – Peter 2010-05-13 14:36:59

+7

鑑於'math.sqrt(x)'等同於'x ** 0.5','math.pow(x,y)'等價於'x ** y',我很驚訝這些倖存下來的冗餘軸在Python 2.x-> 3.0轉換期間運行。在實踐中,我通常將這些數字事物作爲更大計算密集型過程的一部分,並且解釋器對'**'的支持直接轉到字節碼BINARY_POWER,而不是查找'數學',訪問到其屬性'sqrt',然後緩慢的字節碼CALL_FUNCTION,可以在無編碼或可讀性成本的情況下在速度上做出可測量的改進。 – PaulMcG 2010-05-14 07:11:08

22

使用numpy(強烈推薦),你會做:

from numpy import (array, dot, arccos, clip) 
from numpy.linalg import norm 

u = array([1.,2,3,4]) 
v = ... 
c = dot(u,v)/norm(u)/norm(v) # -> cosine of the angle 
angle = arccos(clip(c, -1, 1)) # if you really want the angle 
+2

最後一行可能會導致一個錯誤,因爲我已經找到由於舍入錯誤而退出。因此,如果你點(u,u)/ norm(u)** 2,結果爲1.0000000002,然後arccos失敗(對於反平行矢量也是「有效的」) – BandGap 2012-01-27 11:10:45

+0

我用u = [1,1,1 ]。 u = [1,1,1,1]可以正常工作,但每個維度添加的返回值稍大於或小於1 ... – BandGap 2012-01-27 11:20:06

+1

注意:**這將失敗**(yield'nan')當兩個方向向量是相同的或相反的。查看我的答案以獲得更正確的版本。 – 2012-12-12 21:52:43

68

注意:如果兩個向量要麼向同一方向(例如,(1, 0, 0)(1, 0, 0))或相反方向(例如,(-1, 0, 0)(1, 0, 0))都在這裏其他的答案將會失敗。

這裏是一個能正確處理這些情況的功能:

import numpy as np 

def unit_vector(vector): 
    """ Returns the unit vector of the vector. """ 
    return vector/np.linalg.norm(vector) 

def angle_between(v1, v2): 
    """ Returns the angle in radians between vectors 'v1' and 'v2':: 

      >>> angle_between((1, 0, 0), (0, 1, 0)) 
      1.5707963267948966 
      >>> angle_between((1, 0, 0), (1, 0, 0)) 
      0.0 
      >>> angle_between((1, 0, 0), (-1, 0, 0)) 
      3.141592653589793 
    """ 
    v1_u = unit_vector(v1) 
    v2_u = unit_vector(v2) 
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)) 
+0

使用'np.isnan'而不是數學庫中的那個會更好嗎?理論上它們應該是相同的,但我在實踐中不太確定。無論哪種方式,我會想象它會更安全。 – Hooked 2013-07-15 15:52:59

+0

唯一的區別是,如果輸入是一個數組,那麼np.isnan會做一些明智的事情,這在這裏永遠不會出現。然而,使用'np.isnan'肯定會更清晰(不知道爲什麼我使用'math.isnan' ...),所以我會切換它。 – 2013-07-15 16:03:02

+9

您也可以使用'angle = np.arccos(np.clip(np.dot(v1_u,v2_u), - 1,1))'並跳過if-else業務。 – letmaik 2014-01-13 10:59:37

0

使用numpy的和照顧的帶隙的舍入誤差:

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)。

5

另一種可能是隻使用numpy和它給你的內角

import numpy as np 

p0 = [3.5, 6.7] 
p1 = [7.9, 8.4] 
p2 = [10.8, 4.8] 

''' 
compute angle (in degrees) for p0p1p2 corner 
Inputs: 
    p0,p1,p2 - points in the form of [x,y] 
''' 

v0 = np.array(p0) - np.array(p1) 
v1 = np.array(p2) - np.array(p1) 

angle = np.math.atan2(np.linalg.det([v0,v1]),np.dot(v0,v1)) 
print np.degrees(angle) 

這裏是輸出:

In [2]: p0, p1, p2 = [3.5, 6.7], [7.9, 8.4], [10.8, 4.8] 

In [3]: v0 = np.array(p0) - np.array(p1) 

In [4]: v1 = np.array(p2) - np.array(p1) 

In [5]: v0 
Out[5]: array([-4.4, -1.7]) 

In [6]: v1 
Out[6]: array([ 2.9, -3.6]) 

In [7]: angle = np.math.atan2(np.linalg.det([v0,v1]),np.dot(v0,v1)) 

In [8]: angle 
Out[8]: 1.8802197318858924 

In [9]: np.degrees(angle) 
Out[9]: 107.72865519428085 
相關問題