複數

複數 在 Python

{other: "%{count} 個練習"}

關於 複數

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 世紀透過複數所帶來的一些實用進展。

  1. 半導體晶片。

    • 這些在古典物理中毫無意義,只能用量子力學(QM)來解釋(和設計)。
    • 在量子力學中,萬物依定義都是複數值。(一路往下都是波形)
  2. 快速傅立葉轉換(FFT)演算法。

    • FFT 是複數的一種應用,而且它存在於與聲音傳輸、音訊處理、照片和影片有關的_一切_之中。

    • MP3 和其他音訊格式使用 FFT 進行壓縮,讓更多音訊能塞進更小的儲存空間。

    • JPEG 壓縮和 MP4 影片,以及許多其他影像和視訊格式,也使用 FTT 進行壓縮。

    • FFT 也部署在數位濾波器中,讓手機基地台能把你的個人行動訊號與其他人的分開。

所以,你很可能正在使用每秒依賴複數運算數千次的技術。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟