Python复数complex创建运算与傅里叶变换应用实践
在Python的内置类型中,complex复数类型虽然不如int和float常用,但它在信号处理、图像旋转、电路分析、分形绘图等场景中有着不可替代的作用。多数主流语言并不直接内置复数,往往需要引入第三方库;Python则把复数作为一等公民,支持字面量、构造器以及完整的cmath数学函数库。这篇文章从复数的创建、运算、函数支持到典型代码实践逐步展开,帮助开发者在需要时能直接写出可运行的复数处理逻辑。一、Python中复数的创建方式
复数由实部与虚部构成,形如a + bj。Python沿用了工程领域的惯例,用j而不是数学中的i表示虚数单位,因为i在电路分析中常代表电流。创建复数有两种主要方式。
第一种是字面量方式,虚部系数必须紧贴j。
z1 = 3 + 4j # 实部3 虚部4
z2 = 1 - 2j
z3 = 5j # 纯虚数 相当于0+5j
z4 = 2 + 0j # 实数为2 但类型是complex
z5 = 1 + 1j # 正确写法
# z5 = 1 + j # 错误 这里的j会被当作变量名 导致NameError
第二种是使用complex()函数,可以接收一个或两个数值参数,也可以接收字符串参数。
complex(5) # (5+0j)
complex(3.14) # (3.14+0j)
complex(3, 4) # (3+4j)
complex(-1, 2.5) # (-1+2.5j)
complex('3+4j') # (3+4j)
complex('-1-2j') # (-1-2j)
complex('5j') # 5j
complex('3') # (3+0j)
注意,complex()解析字符串时不允许出现空格,例如complex('3 + 4j')会抛出异常;字符串中也不能使用i代替j。
访问实部和虚部以及取共轭可以使用对象的属性与方法:
z = 3 + 4j
print(z.real) # 3.0 返回float
print(z.imag) # 4.0 返回float
print(z.conjugate()) # (3-4j)
二、复数运算与比较限制
复数的加减乘除、幂运算与普通数值类型一致,可以直接使用运算符。以a = 3 + 4j,b = 1 - 2j为例:
a = 3 + 4j
b = 1 - 2j
print(a + b) # (4+2j)
print(a - b) # (2+6j)
print(a * b) # (11-2j)
print(a / b) # (-1+2j)
print(a ** 2)# (-7+24j)
乘法结果的推算过程:3×1 + 3×(-2j) + 4j×1 + 4j×(-2j) = 3 - 6j + 4j - 8j²,因j² = -1,所以最后是11 - 2j。除法可通过共轭分母化简得到结果。幂运算也遵守同样的复数运算法则。
复数只能判断相等或不等,不能比较大小。这在数学上也有意义,因为复数不是有序域。下面代码会触发TypeError:
a = 3 + 4j
b = 3 + 4j
print(a == b) # True
print(a != b) # False
# print(a > b)# TypeError: '>' not supported between instances of 'complex' and 'complex'
内置函数abs()可用于求复数的模,即sqrt(实部² + 虚部²)。round()不能直接用于复数对象。
三、cmath模块:面向复数的数学函数
Python的math模块面向实数,复数运算需要使用cmath模块。cmath提供复变函数、极坐标转换以及判断函数。
import cmath
z = 3 + 4j
print(abs(z)) # 5.0 模长
print(cmath.sqrt(z)) # (2+1j) 因为(2+j)²=3+4j
print(cmath.polar(z)) # (5.0, 0.9272952180016122) 返回(模, 辐角)
print(cmath.rect(5.0, 0.9272952180016122))# (3+4j) 极坐标转直角坐标
cmath.exp支持欧拉公式形式的指数运算,例如e^(jx) = cos(x) + j*sin(x):
x = 0.5
print(cmath.exp(1j * x))# (0.87758+0.47943j)
cmath模块还包括log、log10、三角函数、反三角函数和双曲函数:
z = 1 + 1j
cmath.exp(z)
cmath.log(z) # 自然对数
cmath.log10(z)
cmath.sin(z)
cmath.cos(z)
cmath.tan(z)
cmath.asin(z)
cmath.acos(z)
cmath.atan(z)
cmath.sinh(z)
cmath.cosh(z)
cmath.tanh(z)
判断与比较方面的工具:
cmath.isinf(complex(float('inf'), 0))# True
cmath.isnan(complex(float('nan'), 0))# True
cmath.isclose(1+2j, 1+2.0000000001j)# True 容差比较
四、复数的实际应用代码实践
信号处理中DFT核心计算会使用复数指数。手写离散傅里叶变换有助于理解频谱形成的原理。下面代码直接定义DFT函数,并分析由50Hz和120Hz正弦波叠加的信号:
import cmath
import math
def discrete_fourier_transform(samples):
n = len(samples)
result = []
for k in range(n):
frequency_component = 0j
for t in range(n):
angle = -2 * cmath.pi * 1j * k * t / n
frequency_component += samples * cmath.exp(angle)
result.append(frequency_component)
return result
sample_rate = 500
duration = 1.0
n_samples = int(sample_rate * duration)
samples = []
for i in range(n_samples):
t = i / sample_rate
value = math.sin(2 * math.pi * 50 * t) + 0.5 * math.sin(2 * math.pi * 120 * t)
samples.append(value)
frequency_spectrum = discrete_fourier_transform(samples)
print('频率分析结果:')
for i in range(10):
magnitude = abs(frequency_spectrum)
frequency = i * sample_rate / n_samples
print(f'{frequency:.0f}Hz: 幅度={magnitude:.2f}')
运行后能从频谱幅度中明显看到50Hz与120Hz两个峰值对应的频率分量。
图像处理或二维坐标系变换中,可以用复数表示平面上的点,实部为x坐标,虚部为y坐标。矩阵旋转因此可以简化为复数乘法:
import math
def rotate_point(x, y, angle_degrees):
point = complex(x, y)
angle_radians = math.radians(angle_degrees)
rotation = complex(math.cos(angle_radians), math.sin(angle_radians))
rotated = point * rotation
return rotated.real, rotated.imag
print(rotate_point(1, 0, 90)) # (0.0, 1.0)
同理,缩放点可以直接乘以常数因子。
电路分析中的交流阻抗也用复数表示:电阻为实数,电感和电容为纯虚数。下面实现RLC串联总阻抗的计算:
import cmath
def impedance_resistor(r):
return complex(r, 0)
def impedance_inductor(inductance, frequency):
omega = 2 * 3.14159 * frequency
return complex(0, omega * inductance)
def impedance_capacitor(capacitance, frequency):
omega = 2 * 3.14159 * frequency
return complex(0, -1 / (omega * capacitance))
def series_impedance(*impedances):
return sum(impedances)
R = 100.0
L = 0.1
C = 10e-6
f = 1000.0
Z_total = series_impedance(
impedance_resistor(R),
impedance_inductor(L, f),
impedance_capacitor(C, f)
)
print(f'总阻抗:{Z_total}')
print(f'阻抗模:{abs(Z_total):.2f}欧姆')
print(f'相位角:{cmath.phase(Z_total):.2f}弧度')
这段代码验证了复数运算在电子仿真中的直接性。
曼德勃罗集是复数迭代的经典示例。用字符串字符着色可以快速在终端生成ASCII图案:
def mandelbrot(c, max_iterations=100):
z = 0j
for n in range(max_iterations):
z = z * z + c
if abs(z) > 2:
return n
return max_iterations
def generate_mandelbrot(width=100, height=40, max_iter=50):
x_min, x_max = -2.0, 1.0
y_min, y_max = -1.0, 1.0
for y in range(height):
line = ''
for x in range(width):
real = x_min + (x / width) * (x_max - x_min)
imag = y_min + (y / height) * (y_max - y_min)
c = complex(real, imag)
m = mandelbrot(c, max_iter)
if m == max_iter:
line += '#'
elif m > max_iter * 0.8:
line += '*'
elif m > max_iter * 0.5:
line += '+'
elif m > max_iter * 0.2:
line += '.'
else:
line += ' '
print(line)
generate_mandelbrot()
执行后终端会出现经典的曼德勃罗集轮廓。
五、类型转换与易错点
复数不能直接转换为int或float,需要先取绝对值或分别取实部与虚部:
z = 3 + 4j
str(z) # '(3+4j)'
abs(z) # 5.0 float类型
# int(z) # TypeError
# float(z) # TypeError
其他数值类型或字符串可以转换为复数,complex(5)、(3.14)或complex('1+2j')都会得到complex对象。
两个常见坑需要关注。第一个坑是求负数的平方根时,math.sqrt会直接报ValueError,必须改用cmath.sqrt,但cmath.sqrt返回的永远是complex类型,即使结果恰为实数:
import cmath
import math
result = cmath.sqrt(-1) # 1j
print(cmath.sqrt(4)) # (2+0j)
如果希望正数返回float、负数返回complex,可以封装一个安全开方函数:
def safe_sqrt(x):
if x >= 0:
return math.sqrt(x)
else:
return cmath.sqrt(x)
print(safe_sqrt(4)) # 2.0
print(safe_sqrt(-4))# 2j
第二个坑是复数的实部和虚部在Python内部总是float类型。即使写成3 + 4j,访问z.real得到的是3.0而不是整数3。这也意味着complex对象可以承载inf和nan等特殊浮点值:
z = 3 + 4j
print(type(z.real))# <class 'float'>
print(type(z.imag))# <class 'float'>
z1 = complex(float('inf'), 0)
z2 = complex(0, float('nan'))
总结:Python内置complex使复数运算免去额外安装科学计算库的成本。创建上字面量最简洁、complex()最灵活;运算上支持四则与幂,但不能比较大小;函数支持依赖cmath模块;实际场景中DFT、图像旋转、交流阻抗与曼德勃罗集都能直接用原生代码落地。写代码时重点注意j的位置、字符解析空格和cmath返回类型的特性,就能避免多数报错。
Re: Python复数complex创建运算与傅里叶变换应用实践
楼主这篇写得很清晰,把复数从创建到运算再到实际应用的路径讲得很顺。尤其是提醒了 `j` 不能单独当虚数用、`complex('3+4j')` 不允许空格这些细节,都是新手容易踩坑的地方。DFT那段示例也很实用,能直观看到复数在频谱分析里的作用。 补充一点小想法:`cmath` 里的函数确实能帮上大忙,但实际做信号处理时,更常用的还是 `numpy.fft`,因为纯 Python 循环计算 DFT 性能会差一些。不过楼主手写一遍能帮助理解公式,很有教学价值。 另外,复数在图像旋转里也很有意思,把坐标点当复数乘上 `e^(iθ)` 就能旋转任意角度。相比矩阵方式,这种写法在某些场景下更直观。 如果后续能再讲讲复数与 `numpy` 的配合,或者相位、频谱可视化,就更完整了。感谢分享!Re: Python复数complex创建运算与傅里叶变换应用实践
楼主的总结很实用!Python把复数当一等公民确实方便,特别是写信号处理或者电路仿真的时候,不用自己封装复数类。之前我用C++做FFT还得找库,Python里直接`complex`一把梭。 补充一点:楼主提到`complex('3+4j')`不允许空格,这点确实容易踩坑。我一般习惯用`complex(real, imag)`传两个参数,尽量避免字符串解析,尤其数据多时性能也更稳。 另外`cmath.polar`和`cmath.rect`在做极坐标转换时特别好用,配合`cmath.exp(1j * angle)`能很直观地理解旋转因子。期待楼主后续能写个逆变换或FFT优化的例子。Re: Python复数complex创建运算与傅里叶变换应用实践
感谢分享,内容很详实,尤其是从创建复数到傅里叶变换应用的完整流程,适合刚接触复数的开发者上手。 我照着跑了一遍,发现手写DFT那段有两个小笔误:一是 `cmath.pi` 应该写成 `math.pi`,因为 `cmath` 模块并没有提供 `pi`;二是累加计算里 `frequency_component += samples * cmath.exp(...)` 这里的 `samples` 应该是 `samples`,否则列表没法直接参与乘法。另外帖子末尾的 `sample_rate` 那段似乎被截断了,可能是发帖时不小心弄的。 整体来说例子很清晰,复数运算和实际信号处理结合得不错,期待后续还能有傅里叶逆变换或频谱可视化相关的补充。
页:
[1]