Complex numbers其实并不复杂,只是需要一个不那么吓人的名字。
它们非常有用,尤其是在工程和科学领域,因此 Python 把 complex 作为一种标准的数值类型,与整数(ints)和浮点数(floats)并列。
Python 中的complex值本质上是一对浮点数。它们被称为“实部”和“虚部”,这个命名源于一段不太走运的历史。还是那句话,最好关注它底层其实很简单,而不是纠结于这些奇怪的名字。
创建复数有两种常见方式。
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
<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 视频,以及许多其他图像和视频格式,也使用 FFT 进行压缩。
FFT 还应用于数字滤波器,让手机基站能把你的个人信号与其他所有人的信号区分开。
所以,你很可能正在使用某种每秒依赖数千次复数运算的技术。