- 程序員數學:用Python學透線性代數和微積分
- (美)保羅·奧蘭德
- 4722字
- 2022-01-10 14:44:10
2.3 平面上的角度和三角學
到目前為止,我們已經使用了兩把“尺子”(稱為軸和
軸)來測量平面上的向量。從原點出發的箭頭包含了水平和垂直方向上的可測量位移。實際上,與其使用兩把尺子,還不如使用一把尺子和一把量角器。以向量(4, 3)為例,我們可以測量出它的長度為5個單位,然后用量角器確定方向,如圖2-34所示。

圖2-34 使用量角器測量方向
這個向量的長度為5個單位,方向為從軸正半軸逆時針旋轉約37°。像原始坐標對一樣,可以用一個新的數對(5, 37°)唯一地確定該向量。這種形式的坐標稱為極坐標(polar coordinates),和我們到現在為止所使用的笛卡兒坐標(Cartesian coordinates)一樣,能很好地描述平面上的點。
有時候,比如做向量加法時,使用笛卡兒坐標更簡單;而其他時候,極坐標更實用,特別是進行向量旋轉時。在寫代碼時,因為沒有所謂的刻度尺或量角器,所以只能依賴三角函數。
2.3.1 從角度到分量
反過來思考一下:想象我們已經有了一個角度和一個距離,比如116.57°和3。這兩者定義了一對極坐標(3, 116.57°),那么,這個向量的笛卡兒坐標是什么呢?
首先,可以將量角器放在軸上并將刻度0對準原點,以確定向量的方向。從
軸正半軸逆時針旋轉116.57°,并在這個方向上畫一條線(見圖2-35)。向量(3, 116.57°)就在這條線上的某處。

圖2-35 用量角器測量116.57°
然后用一把尺子,在這個方向上測量出一個距離原點3個單位的點。如圖2-36所示,一旦找到這個點,就可以測量出向量的分量,得到近似坐標(-1.34, 2.68)。

圖2-36 用尺子測量距離原點3個單位的點的坐標
116.57°這個角度并不是隨機選擇的,從原點開始沿著這個方向移動,每向左走1個單位,就會上升2個單位。大致位于這條線上的向量包括(-1, 2)和(-3, 6),當然還有(-1.34,2.68),這些向量的坐標長度是
坐標長度的2倍(見圖2-37)。

圖2-37 向116.57°所表示的方向移動,每向左移動1個單位,就向上移動2個單位
在116.57°這個方向上,縱橫坐標的比值恰好約等于-2。我們不可能總是幸運地得到一個整數的比值,但每個角度都對應一個固定的比值。圖2-38展示了另一個角度,200°。它給出的固定比值為0.36,即每-1個水平單位對應-0.36個垂直單位。

圖2-38 在不同角度下,每單位水平距離對應多少垂直距離
給定一個角度,該角度上向量的坐標將有一個固定的比值。這個比值叫作角的正切,正切函數寫作tan。到目前為止,你已經看到了正切的幾個近似值。
在這里,為表示近似相等,用符號≈而不是=。正切函數是一個三角(trigonometric1)函數,因為它可以用來測量三角形。目前我們還沒有告訴你如何計算正切,只指出了幾個值。不過Python內置了正切函數,很快就會介紹到,請不用擔心。
1trigonometric中的trigon指三角形,metric指測量。
正切函數顯然與我們最初的問題有關,即為給定角度和距離的向量尋找笛卡兒坐標。但它實際上并不給出坐標,只給出其比值。在這一點上,另兩個三角函數很有幫助:正弦(sin)和余弦(cos)。從角度和距離的關系來看,角的正切等于垂直距離除以水平距離(見圖2-39)。

圖2-39 向量的距離和角度示意圖
相比之下,正弦函數和余弦函數給出了向量的垂直距離、水平距離和整體距離之間的關系,其定義如下面的公式所示。

來看一個具體的示例(見圖2-40)。對于一個37°的角,上面的點(4, 3)距離原點5個單位。

圖2-40 用量角器測量(4, 3)和軸的夾角
在37°角的方向上每移動5個單位,就會垂直移動大約3個單位,所以:
在37°角的方向上每移動5個單位,就會水平移動大約4個單位,所以:
這是將極坐標轉換為對應的笛卡兒坐標的一般方法。如果知道一個角(希臘字母theta,常用來表示角)的正弦和余弦,以及在該方向上的距離
,則笛卡兒坐標為
,如圖2-41所示。

圖2-41 圖解直角三角形中極坐標到笛卡兒坐標的轉換
2.3.2 Python中的三角學和弧度
讓我們把三角學知識轉化為Python代碼:實現一個函數,接收一對極坐標(長度值和角度值)并輸出一對笛卡兒坐標(分量和
分量的長度)。
主要的問題是Python內置的三角函數與我們使用的單位不同。例如,我們期望,但Python給出結果卻大不相同。
>>> from math import tan
>>> tan(45)
1.6197751905438615
Python不使用角度,事實上大多數數學家也不使用角度。他們使用弧度(radian)來替代角度,換算系數是:
1弧度 ≈ 57.296°
之所以這樣,是因為一個特殊的數π,它的值約為3.141 59。正是它搭建了角度和弧度之間的橋梁。
π弧度 = 180°
2π弧度 = 360°
繞圓半圈的弧度為π,整圈的弧度是2π,分別與半徑為1的圓的半周長和周長一致(見圖2-42)。

圖2-42 半周長的弧度是π,周長的弧度是2π
可以把弧度看作另一種比值:對于一個給定的角度,它的弧度值表明你已經繞過的半徑數。正因為這種特性,弧度本身并沒有單位。注意到45° = π/4(弧度),所以正確的處理方式應該如下所示。
>>> from math import tan, pi
>>> tan(pi/4)
0.9999999999999999
現在我們可以用Python提供的三角函數實現一個to_cartesian
函數,接收一對極坐標并返回相應的笛卡兒坐標。
from math import sin, cos
def to_cartesian(polar_vector):
length, angle = polar_vector[0], polar_vector[1]
return (length*cos(angle), length*sin(angle))
利用這一點,可以驗證沿著37°角的方向移動5個單位可以接近點(4, 3)。
>>> from math import pi
>>> angle = 37*pi/180
>>> to_cartesian((5,angle))
(3.993177550236464, 3.0090751157602416)
現在可以將極坐標轉換為笛卡兒坐標了,下面來看看如何將笛卡兒坐標轉換為極坐標。
2.3.3 從分量到角度
給定一對笛卡兒坐標,如(-2, 3),可以使用勾股定理計算向量的長度,即。這是我們要找的極坐標對中的第一個坐標。第二個坐標是角度,可以用
表示,指出這個向量的方向(見圖2-43)。

圖2-43 向量(-2, 3)指向的角度
為了得到,這里提供一些已知條件:
,
,
。剩下的就是找到一個滿足這些條件的
值。你可以暫停一下,試試估算這個角度值。
理想情況下,我們希望有一種更有效的方法。如果有一個函數可以接收的值并返回
,那就太好了。說起來容易做起來難,但Python的
math.asin
函數幫助我們實現了這一點。這是一個名為反正弦(asin)的反三角函數實現,能返回符合要求的值。
>>> from math import asin
>>> sin(1)
0.8414709848078965
>>> asin(0.8414709848078965)
1.0
到目前為止,沒有什么問題。但角的正弦呢?
>>> from math import sqrt
>>> asin(3/sqrt(13))
0.9827937232473292
這個弧度對應的角度大概是56.3°,如圖2-44所示,這個方向是錯誤的!

圖2-44 Python的math.asin
函數看起來返回了錯誤的角度
math.asin
給出的答案并沒有錯,另一個點(2, 3)確實位于這個方向。它距離原點的長度是,所以這個角的正弦值也是
。這就是為什么
math.asin
并不完美,因為不同角度可以有相同的正弦。
反余弦(acos)在Python中被實現為math.acos
,可以用來求出正確的值。
>>> from math import acos
>>> acos(-2/sqrt(13))
2.1587989303424644
這個弧度對應的角度大約為123.7°,可以使用量角器確認是正確的。但這只是偶然,因為還有其他的角度可以給出相同的余弦。例如,(-2, -3)離原點距離也為,所以它所在的角度與
的余弦同為
。為了找到我們真正想要的
的值,必須確保它的正弦和余弦與我們的期望值一致。Python返回的弧度約為2.159,滿足這個要求。
>>> cos(2.1587989303424644)
-0.5547001962252293
>>> -2/sqrt(13)
-0.5547001962252291
>>> sin(2.1587989303424644)
0.8320502943378435
>>> 3/sqrt(13)
0.8320502943378437
反正弦、反余弦或反正切函數都不足以找到平面內某個點與軸正半軸的夾角。你在上中學的時候肯定學過如何找到正確的點,這里按下不表,直接切入正題——Python可以幫你完成這個工作。
math.atan2
函數接收平面上一個點的笛卡兒坐標(按相反的順序)作為參數,返回對應的弧度。例如:
>>> from math import atan2
>>> atan2(3,-2)
2.158798930342464
抱歉之前賣了個關子,但這樣做是為了讓你了解使用反三角函數的潛在陷阱。總而言之,三角函數是很難反解的。多個不同的輸入可以產生相同的輸出,所以一個輸出并不能對應唯一的輸入。讓我們完成一開始要寫的函數:一個從笛卡兒坐標到極坐標的轉換器。
def to_polar(vector):
x, y = vector[0], vector[1]
angle = atan2(y,x)
return (length(vector), angle)
可以通過一些簡單的示例來驗證。to_polar((1,0))
應該是軸正半軸上的1個單位,角度為0。事實上,該函數返回的弧度為0,長度為1。
>>> to_polar((1,0))
(1.0, 0.0)
(這里的輸入和輸出相同是巧合,它們的幾何意義不同。)同樣,我們也可以得到(-2, 3)的答案。
>>> to_polar((-2,3))
(3.605551275463989, 2.158798930342464)
2.3.4 練習
練習2.27:確認笛卡兒坐標(-1.34, 2.68)對應的向量的長度約為3。
解:
>>> length((-1.34,2.68)) 2.9963310898497184
十分近似了!
練習2.28:圖2-45中是一條從
正半軸開始按逆時針方向旋轉22°角的直線。根據圖2-45,
的近似值是多少?
![]()
圖 2-45
解:直線經過點(10, 4)附近,所以4/10 = 0.4是
的合理近似值,如圖2-46所示。
![]()
圖 2-46
練習2.29:轉換問題的角度,假設我們知道了一個向量的長度和方向,想找到它的分量該如何做呢?一個長度為15的向量指向37°角,其
分量和
分量是多少?
解:37°的正弦值大約是3/5,表示沿這個角度每移動5個單位,就會垂直向上移動3個單位。所以,長度為15的向量的垂直分量為3/5·15,即9。
37°的余弦約等于4/5,表示在這個方向上每移動5個單位,就會水平向右移動4個單位,所以水平分量是4/5·15,即12。綜上所述,極坐標(15, 37°)與笛卡兒坐標(12, 9)大致對應。
練習2.30:假設從原點出發,沿著從
軸正半軸逆時針旋轉125°的方向移動8.5個單位,那么最終坐標是什么?已知
、
,請畫圖來表示走過的角度和路徑。
解:
圖2-47顯示了最終坐標為 (-4.879, 6.962)。
![]()
圖 2-47
練習2.31:0°、90°和180°的正弦和余弦各是多少?換句話說,在這些方向上,每單位距離經過多少個垂直和水平單位?
解:對于0°,沒有垂直距離,所以
;而每移動1個單位的距離就經過
軸正半軸方向上的1個單位,所以
。
對于90°(逆時針轉1/4圈),每移動1個單位的距離就經過
軸正半軸方向上的1個單位,所以
,而
。
最后,對于180°,每移動1個單位的距離都經過
軸負半軸方向上的1個單位,所以
,而
。
練習2.32:圖2-48對于一個直角三角形給出了一些精確的測量數據。首先,確認這些長度在直角三角形中的有效性,因為它們必須滿足勾股定理。然后,用圖中的數據計算
、
和
的值,精確到小數點后三位。
![]()
圖 2-48
解:代入公式,證實這些邊長確實滿足勾股定理。
根據正弦、余弦和正切的定義,由邊長的比例得出近似的三角函數值。
練習2.33:從另一個角度觀察上一個練習中的三角形,用它計算
、
和
的值,精確到小數點后三位。
解:旋轉并鏡像上一個練習中的三角形,這對它的邊長和角度沒有影響(見圖2-49)。
![]()
圖2-49 變換上一個練習中的三角形后得到的三角形
調換水平和垂直分量后重新計算,通過邊長的比值得出60°角對應的三角函數值。
練習2.34:已知50°的余弦值是0.643。
的值是多少,
的值又是多少?通過畫圖來計算。
解:已知50°的余弦值是0.643,可以畫出如圖2-50所示的三角形。
![]()
圖 2-50
也就是說,已知兩個邊長的比值:0.643/1 = 0.643。要找到未知邊長,可以使用勾股定理。
在已知邊長的情況下,
,則
。
練習2.35:116.57°對應的弧度是多少?用Python計算這個角的正切值,并確認它約等于-2。
解:116.57°·(1弧度/57.296°) ≈ 2.035弧度。
>>> from math import tan >>> tan(2.035) -1.9972227673316139
練習2.36:cos(10π/6)和sin(10π/6)的值為正還是為負?使用Python計算它們的值并確認。
解:一個完整的圓的弧度是2π,所以π/6是一個圓的1/12。可以想象成把一張比薩切成12塊,從
正半軸開始逆時針數,角10π/6表示只差兩塊就轉完了。這說明它指向右下方,所以余弦應該是正值,而正弦應該是負值,因為這個方向的水平分量和垂直分量分別是正的和負的。
>>> from math import pi, cos, sin >>> sin(10*pi/6) -0.8660254037844386 >>> cos(10*pi/6) 0.5000000000000001
練習2.37:用下面的列表推導式創建1000個極坐標對應的點。
[(cos(5*x*pi/500.0), 2*pi*x/1000.0) for x in range(0,1000)]
在Python代碼中,將這些點轉換為笛卡兒坐標,并用線段依次將其連接起來,從而畫出一幅畫。
解:代碼如下所示。
polar_coords = [(cos(x*pi/100.0), 2*pi*x/1000.0) for x in range(0,1000)] vectors = [to_cartesian(p) for p in polar_coords] draw(Polygon(*vectors, color=green))
結果是一朵五瓣的花,如圖2-51所示。
![]()
圖2-51 將1000個點連接而成的圖是一朵花
練習2.38:通過“猜測檢查法”(guess-and-check)找出(-2, 3)對應的弧度(見圖2-52)。
![]()
圖2-52 點(-2, 3)對應的弧度是多少
提示:顯然答案在π/2和π之間。在這個區間內,正切的絕對值總是隨著弧度的增大而減小。
解:這是一個在π/2和π之間進行猜測和檢查的示例,找一個正切值接近-3/2 = -1.5的角。
>>> from math import tan, pi >>> pi, pi/2 (3.141592653589793, 1.5707963267948966) >>> tan(1.8) -4.286261674628062 >>> tan(2.5) -0.7470222972386603 >>> tan(2.2) -1.3738230567687946 >>> tan(2.1) -1.7098465429045073 >>> tan(2.15) -1.5289797578045665 >>> tan(2.16) -1.496103541616277 >>> tan(2.155) -1.5124173422757465 >>> tan(2.156) -1.5091348993879299 >>> tan(2.157) -1.5058623488727219 >>> tan(2.158) -1.5025996395625054 >>> tan(2.159) -1.4993467206361923
結果肯定在2.158和2.159之間。
練習2.39:在平面上找到另一個與
有相同正切值(即-3/2)的點。使用Python的反正切函數
math.atan
來求這個點的弧度值。解:另一個正切值為-3/2的點是(3, -2)。Python的
math.atan
函數返回了這個點對應的弧度。>>> from math import atan >>> atan(-3/2) -0.982793723247329
也就是順時針方向轉動不到1/4圈。
練習2.40:不使用Python,算出笛卡兒坐標(1, 1)和(1, -1)對應的極坐標。找到答案之后,使用
to_polar
來檢查一下。解:極坐標中,(1, 1)變成了
,(1, -1)變成了
。
兩個向量之間的夾角是它們與
軸所成角度的和或差。在下一個小項目中,會提高一些難度。
練習2.41(小項目):如圖2-53所示,恐龍嘴巴的夾角是多少?腳趾的夾角是多少?尾巴的夾角是多少?
![]()
圖2-53 恐龍圖形中一些可以測量或計算的夾角
- PHP動態網站程序設計
- Go Web編程
- Java 9 Concurrency Cookbook(Second Edition)
- 算法零基礎一本通(Python版)
- 零基礎玩轉區塊鏈
- OpenCV 3和Qt5計算機視覺應用開發
- Java程序設計與計算思維
- 微信公眾平臺開發:從零基礎到ThinkPHP5高性能框架實踐
- INSTANT OpenNMS Starter
- Web前端應用開發技術
- Scala編程(第5版)
- Python自然語言理解:自然語言理解系統開發與應用實戰
- Mastering Apache Camel
- Get Your Hands Dirty on Clean Architecture
- 交互設計師成長手冊:從零開始學交互