脚本专家 发表于 2026-9-7 10:00:00

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返回类型的特性,就能避免多数报错。

热心网友4 发表于 2026-9-7 19:00:00

Re: Python复数complex创建运算与傅里叶变换应用实践

楼主这篇写得很清晰,把复数从创建到运算再到实际应用的路径讲得很顺。尤其是提醒了 `j` 不能单独当虚数用、`complex('3+4j')` 不允许空格这些细节,都是新手容易踩坑的地方。DFT那段示例也很实用,能直观看到复数在频谱分析里的作用。 补充一点小想法:`cmath` 里的函数确实能帮上大忙,但实际做信号处理时,更常用的还是 `numpy.fft`,因为纯 Python 循环计算 DFT 性能会差一些。不过楼主手写一遍能帮助理解公式,很有教学价值。 另外,复数在图像旋转里也很有意思,把坐标点当复数乘上 `e^(iθ)` 就能旋转任意角度。相比矩阵方式,这种写法在某些场景下更直观。 如果后续能再讲讲复数与 `numpy` 的配合,或者相位、频谱可视化,就更完整了。感谢分享!

热心网友4 发表于 2026-9-7 19:00:00

Re: Python复数complex创建运算与傅里叶变换应用实践

楼主的总结很实用!Python把复数当一等公民确实方便,特别是写信号处理或者电路仿真的时候,不用自己封装复数类。之前我用C++做FFT还得找库,Python里直接`complex`一把梭。 补充一点:楼主提到`complex('3+4j')`不允许空格,这点确实容易踩坑。我一般习惯用`complex(real, imag)`传两个参数,尽量避免字符串解析,尤其数据多时性能也更稳。 另外`cmath.polar`和`cmath.rect`在做极坐标转换时特别好用,配合`cmath.exp(1j * angle)`能很直观地理解旋转因子。期待楼主后续能写个逆变换或FFT优化的例子。

热心网友4 发表于 2026-9-7 19:00:00

Re: Python复数complex创建运算与傅里叶变换应用实践

感谢分享,内容很详实,尤其是从创建复数到傅里叶变换应用的完整流程,适合刚接触复数的开发者上手。 我照着跑了一遍,发现手写DFT那段有两个小笔误:一是 `cmath.pi` 应该写成 `math.pi`,因为 `cmath` 模块并没有提供 `pi`;二是累加计算里 `frequency_component += samples * cmath.exp(...)` 这里的 `samples` 应该是 `samples`,否则列表没法直接参与乘法。另外帖子末尾的 `sample_rate` 那段似乎被截断了,可能是发帖时不小心弄的。 整体来说例子很清晰,复数运算和实际信号处理结合得不错,期待后续还能有傅里叶逆变换或频谱可视化相关的补充。
页: [1]
查看完整版本: Python复数complex创建运算与傅里叶变换应用实践