Complex numbers其實一點也不複雜。
它們只是需要一個不那麼嚇人的名字。
它們非常實用,尤其在工程和科學領域,所以 Python 把 complex 納入標準數值型態,與整數(ints)和浮點數(floats)並列。
Python 中的 complex 值本質上是一對浮點數。
因為一些不幸的歷史原因,它們被稱為「實部」和「虛部」。
同樣地,最好把重點放在背後簡單的概念上,而不是那些奇怪的名字。
建立複數有兩種常見方法。
1)complex(real, imag) 建構函式接受兩個 float 參數:
>>> z1 = complex(1.5, 2.0)
>>> z1
(1.5+2j)
建構函式也能解析字串輸入。 這有個奇怪的限制:只要字串裡含有空格就會失敗。
>>> complex('4+2j')
(4+2j)
>>> complex('4 + 2j')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: complex() arg is a malformed string
2)複數也可以用 <real part> + <complex part>j 字面值來指定;如果實部為零,也可以只寫 <complex part>j:
>>> z2 = 2.0 + 1.5j
>>> z2
(2+1.5j)
最終結果和使用 complex() 建構函式完全相同。
關於複數的虛部,有兩條規則:
它以 j 表示(而不是數學教科書裡可能看到的 i)。
j 必須緊接在數字後面,以免 Python 把它看成變數名稱。必要時,請使用 1j。
>>> j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined
>>> 1j
1j
>>> type(1j)
<class 'complex'>
大多數工程師都樂於使用 j。
大多數科學家和數學家偏好以數學記號 i 表示 虛數,但這個記號和電機工程中代表 電流 的 i 衝突。
所以在設計 Python 時,電機工程師勝出了。
若要個別存取複數的各個部分:
>>> z2.real
2.0
>>> z2.imag
1.5
任一部分都可以是零,數學家可能會因此說這個數是「純實數」或「純虛數」。 不過在 Python 裡,它仍然是一個複數:
>>> complex(0, 1)
1j
>>> type(complex(0, 1))
<class 'complex'>
>>> complex(1, 0)
(1+0j)
你可能聽過「i(或 j)是 -1 的平方根」。
目前,這只代表虛部_依定義_滿足以下等式
1j * 1j == -1 # => True
這是個簡單的概念,卻會帶來有趣的結果。
大部分用在浮點數和整數上的 operators 也適用於複數:
>>> z1 = (1.5+2j)
>>> z2 = (2+1.5j)
>>> z1 + z2 # addition
(3.5+3.5j)
>>> z1 - z2 # subtraction
(-0.5+0.5j)
>>> z1 * z2 # multiplication
6.25j
>>> z1 / z2 # division
(0.96+0.28j)
>>> z1 ** 2 # exponentiation
(-1.75+6j)
>>> 2 ** z1 # another exponentiation
(0.5188946835878313+2.7804223253571183j)
>>> 1j ** 2 # j * j == -1
(-1+0j)
解釋複數乘法和除法的規則超出了本概念的範圍(而且你不太可能需要經常「手算」這些運算)。
如果你想深入這個主題,任何關於複數的數學或電機工程入門介紹都會涵蓋這些內容。
或者,Exercism 有一個 Complex Numbers 練習,讓你從基本原理出發,用這些運算自行實作一個複數類別。
複數___不能___做整數除法,所以 // 和 % 運算子以及 divmod() 函式在複數型態上都會失敗。
有兩個為數值型態實作的函式,在處理複數時非常實用:
<complex number>.conjugate() 只是把複數虛部的正負號反轉(從 + 變成 -,或反過來)。
abs(<complex number>) 保證會回傳一個沒有虛部的實數。>>> z1
(1.5+2j)
>>> z1.conjugate() # flip the z1.imag sign
(1.5-2j)
>>> abs(z1) # sqrt(z1.real ** 2 + z1.imag ** 2)
2.5
cmath 模組Python 標準函式庫有一個 math 模組,裡面滿是處理實數的實用功能。
它也有對應的 cmath 模組,用來處理複數。
我們鼓勵你瀏覽這個模組並動手實驗,不過主要的類別有:
以下是使用一些常數的範例:
>>> import cmath
>>> euler = cmath.exp(1j * cmath.pi) # Euler's equation
>>> euler.real
-1.0
>>> round(euler.imag, 15) # round to 15 decimal places
0.0
所以一個含有自然界三個最重要常數 e、i(或 j)和 pi 的簡單運算式,會得到結果 -1。
有些人相信這是整個數學中最美的結果。
它可追溯到 1740 年左右。
除非你有興趣,否則可以跳過這個部分。
在 16 世紀,它確實又奇怪又新穎。
500 年後,它已成為大多數工程和自然科學的核心。
事實證明,複數是描述任何會旋轉的事物、或任何具有波動性質的事物最簡單的方法。 所以它們廣泛應用於電機工程、音訊處理、物理、電腦遊戲和導航等等,這裡只舉了其中幾個應用。
你能看見事物在旋轉。 複數或許不能讓世界運轉,但它們很擅長解釋世界運轉所帶來的_種種現象_:看看任何一張大型風暴的衛星影像就知道了。
沒那麼顯而易見的是,聲音具有波動性,光具有波動性,無線電訊號具有波動性,甚至你國家的經濟至少也有一部分具有波動性。
許多這類波動處理可以用三角函式(sin() 和 cos())完成,但很快就會變得一團亂。
複數指數處理起來___容易得多___。
只有當你住在洞穴裡、靠採集食物為生時,這句話才成立。
如果你正在任何一種螢幕上閱讀這段文字,你就完全依賴著 20 世紀透過複數所帶來的一些實用進展。
半導體晶片。
快速傅立葉轉換(FFT)演算法。
FFT 是複數的一種應用,而且它存在於與聲音傳輸、音訊處理、照片和影片有關的_一切_之中。
MP3 和其他音訊格式使用 FFT 進行壓縮,讓更多音訊能塞進更小的儲存空間。
JPEG 壓縮和 MP4 影片,以及許多其他影像和視訊格式,也使用 FTT 進行壓縮。
FFT 也部署在數位濾波器中,讓手機基地台能把你的個人行動訊號與其他人的分開。
所以,你很可能正在使用每秒依賴複數運算數千次的技術。