复数

复数 属于 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
  1. 复数可以写成 <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)来解释和设计。
    • 在 QM 中,一切按定义都是复数值。(一路到底,全是波形)
  2. 快速傅里叶变换(FFT)算法。

    • FFT 是复数的一个应用,凡是与声音传输、音频处理、照片和视频有关的东西,处处都有它。

    • MP3 和其他音频格式使用 FFT 进行压缩,从而让更多音频能放进更小的存储空间。

    • JPEG 压缩和 MP4 视频,以及许多其他图像和视频格式,也使用 FFT 进行压缩。

    • FFT 还应用于数字滤波器,让手机基站能把你的个人信号与其他所有人的信号区分开。

所以,你很可能正在使用某种每秒依赖数千次复数运算的技术。

通过 GitHub 编辑 该链接会在新窗口或标签页中打开