开场:为什么你要学这个?
嘿,朋友。如果你是在找复数运算的资料,大概率不是数学爱好者,就是正在搞信号处理、电路分析、量子计算或者游戏开发(比如旋转矩阵)的程序员。Python 的复数支持虽然看起来不起眼,但里面有几个坑,新手踩进去能抓狂半天。今天咱们不整那些虚的,直接上干货,用代码说话。
先给你一个直观的感受:Python 里的复数长这样 —— 3 + 4j。注意那个 j,不是 i。这是工程和物理界的惯例,数学书里用 i,代码里用 j。别搞混了。
一、基础操作:加减乘除全攻略
1.1 创建复数
有三种方式,我推荐都用过,但最常用的是第一种和第二种混合用。
# 方式1:直接写,注意j是虚部单位
z1 = 3 + 4j
z2 = 1 - 2j
# 方式2:用 complex() 构造函数
# 注意:complex(3, 4) 返回 3+4j,但 complex('3+4j') 也能解析字符串
z3 = complex(3, 4)
z4 = complex('1-2j')
# 方式3:从实数和虚部分离创建
real = 5
imag = 6
z5 = complex(real, imag)
print(f"z1 = {z1}")
print(f"z2 = {z2}")
print(f"z3 = {z3}")
print(f"z4 = {z4}")
print(f"z5 = {z5}")
输出:
z1 = (3+4j)
z2 = (1-2j)
z3 = (3+4j)
z4 = (1-2j)
z5 = (5+6j)
注意:print 输出的时候,Python 默认会给整个复数加上括号。这是为了清晰,避免歧义。比如 3+4j 和 3-4j,不加括号可能看花眼。
1.2 加减运算
复数的加减法就是实部加实部,虚部加虚部,跟向量加法一模一样。
z1 = 3 + 4j
z2 = 1 - 2j
# 加法
z_sum = z1 + z2
print(f"加法: {z1} + {z2} = {z_sum}") # (4+2j)
# 减法
z_diff = z1 - z2
print(f"减法: {z1} - {z2} = {z_diff}") # (2+6j)
# 验证一下:(3+1) + (4-2)j = 4+2j,没错
输出:
加法: (3+4j) + (1-2j) = (4+2j)
减法: (3+4j) - (1-2j) = (2+6j)
1.3 乘法运算
乘法稍微复杂一点。公式是:(a+bi)(c+di) = (ac-bd) + (ad+bc)i。
Python 帮你算好了,你不用手动推导。
z1 = 3 + 4j
z2 = 1 - 2j
z_mul = z1 * z2
print(f"乘法: {z1} * {z2} = {z_mul}")
# 手动验证:
# 实部 = 3*1 - 4*(-2) = 3 + 8 = 11
# 虚部 = 3*(-2) + 4*1 = -6 + 4 = -2
# 结果应该是 11-2j
输出:
乘法: (3+4j) * (1-2j) = (11-2j)
小技巧:你可以用 .real 和 .imag 属性来提取实部和虚部。
print(f"实部: {z_mul.real}") # 11.0
print(f"虚部: {z_mul.imag}") # -2.0
1.4 除法运算
除法是最容易出错的地方。公式是:(a+bi)/(c+di) = [(ac+bd) + (bc-ad)i] / (c²+d²)。
Python 的 / 运算符会自动处理这个。
z1 = 3 + 4j
z2 = 1 - 2j
z_div = z1 / z2
print(f"除法: {z1} / {z2} = {z_div}")
# 手动验证:
# 分母 = 1² + (-2)² = 1 + 4 = 5
# 实部 = (3*1 + 4*(-2)) / 5 = (3-8)/5 = -1.0
# 虚部 = (4*1 - 3*(-2)) / 5 = (4+6)/5 = 2.0
# 结果应该是 -1+2j
输出:
除法: (3+4j) / (1-2j) = (-1+2j)
1.5 取共轭、模和幅角
这三个在工程和物理里超级常用。
z = 3 + 4j
# 共轭复数:把虚部变号
z_conj = z.conjugate()
print(f"共轭: {z} -> {z_conj}") # (3-4j)
# 模(绝对值):sqrt(a² + b²)
z_mod = abs(z)
print(f"模: |{z}| = {z_mod}") # 5.0
# 幅角(相位):atan2(b, a),单位是弧度
z_phase = phase(z) # 需要 from math import phase
# 或者用 cmath.phase
import cmath
z_phase = cmath.phase(z)
print(f"幅角: arg({z}) = {z_phase} 弧度 = {z_phase * 180 / 3.14159265} 度")
输出:
共轭: (3+4j) -> (3-4j)
模: |3+4j| = 5.0
幅角: arg(3+4j) = 0.9272952180016122 弧度 = 53.13010235415598 度
二、常见陷阱和报错避坑
2.1 陷阱一:j 前面必须带数字
这是新手最常犯的错误。
# ❌ 错误写法
z_bad = 3 + j # NameError: name 'j' is not defined
# ✅ 正确写法
z_good = 3 + 1j # 虚部系数是1,不能省略
原理:Python 把 j 当成变量名,而不是数学常数。1j 才是复数单位。
2.2 陷阱二:complex() 构造函数的字符串解析
# ✅ 这种写法没问题
z1 = complex('3+4j')
z2 = complex('3-4j')
z3 = complex('-3+4j')
z4 = complex('-3-4j')
# ❌ 但空格会导致问题
try:
z_bad = complex('3 + 4j') # ValueError: complex() arg is a malformed string
except ValueError as e:
print(f"报错: {e}")
# ✅ 正确写法:去掉空格
z_good = complex('3+4j')
注意:complex() 解析字符串时,不能有空格。但直接写 3+4j 这种表达式是可以有空格的,因为 Python 解释器会先处理运算符。
2.3 陷阱三:除法由整数的复数除法
# 在 Python 3 中,复数除法默认返回复数
z1 = 10 + 0j
z2 = 2 + 0j
result = z1 / z2
print(result) # (5+0j) —— 注意,是复数,不是整数5
# 如果你想要纯实数,需要手动转换
result_real = result.real
print(result_real) # 5.0
2.4 陷阱四:虚部为0时的显示问题
z1 = 5 + 0j
z2 = 5 - 0j # 注意,这是负零
print(z1) # 5j
print(z2) # (5-0j) —— 奇怪吧?
# 这是因为浮点数的负零问题
print(z1 == z2) # True,值相等
print(z1 is z2) # False,不是同一个对象
建议:如果你关心这个,可以用 round() 或者 cmath 模块的函数来清理。
2.5 陷阱五:用数学函数时忘记 import cmath
import math
import cmath
# ❌ 错误:math 模块不支持复数
try:
result = math.sqrt(-1) # ValueError: math domain error
except ValueError as e:
print(f"math.sqrt 报错: {e}")
# ✅ 正确:用 cmath 模块
result = cmath.sqrt(-1)
print(f"cmath.sqrt(-1) = {result}") # 1j
常用 cmath 函数:
cmath.sqrt(z)—— 平方根cmath.exp(z)—— 指数cmath.log(z)—— 自然对数cmath.sin(z),cmath.cos(z),cmath.tan(z)—— 三角函数cmath.phase(z)—— 幅角cmath.polar(z)—— 转为极坐标 (r, theta)cmath.rect(r, theta)—— 从极坐标转回复数
import cmath
z = 1 + 1j
# 求平方根
print(cmath.sqrt(z)) # (1.09868411346781+0.45508986056222733j)
# 求指数
print(cmath.exp(z)) # (-1.1312043837568135+2.4717266720048188j)
# 极坐标转换
r, theta = cmath.polar(z)
print(f"模: {r}, 幅角: {theta}") # 模: 1.4142135623730951, 幅角: 0.7853981633974483
# 从极坐标转回复数
z_back = cmath.rect(r, theta)
print(z_back) # (1+1j)
三、实际应用示例
3.1 信号处理:正弦波的复数表示
import cmath
import math
# 用复数表示正弦波:A * e^(j(ωt + φ))
A = 5 # 振幅
omega = 2 * math.pi * 10 # 角频率,10Hz
phi = math.pi / 4 # 初相位
t = 0.01 # 时间
# 复数形式
z = A * cmath.exp(1j * (omega * t + phi))
print(f"复数表示: {z}")
# 实部就是实际信号值
signal_value = z.real
print(f"信号值: {signal_value}")
# 验证:A * cos(ωt + φ)
expected = A * math.cos(omega * t + phi)
print(f"期望值: {expected}")
print(f"误差: {abs(signal_value - expected)}") # 应该接近0
3.2 电路分析:阻抗计算
# RLC 串联电路的阻抗
# Z = R + jωL + 1/(jωC) = R + j(ωL - 1/(ωC))
R = 100 # 电阻,欧姆
L = 0.01 # 电感,亨利
C = 1e-6 # 电容,法拉
f = 1000 # 频率,赫兹
omega = 2 * math.pi * f
# 感抗和容抗
X_L = omega * L # 感抗
X_C = 1 / (omega * C) # 容抗
# 总阻抗
Z = R + 1j * (X_L - X_C)
print(f"阻抗 Z = {Z} 欧姆")
print(f"模 |Z| = {abs(Z)} 欧姆")
print(f"幅角 arg(Z) = {cmath.phase(Z)} 弧度 = {cmath.phase(Z) * 180 / math.pi} 度")
3.3 几何变换:复数旋转
import cmath
import math
# 用复数旋转一个点
point = 3 + 4j # 点 (3, 4)
angle = math.pi / 2 # 旋转90度
# 旋转矩阵用复数乘法实现
rotation = cmath.exp(1j * angle)
rotated_point = point * rotation
print(f"原点: {point}")
print(f"旋转后: {rotated_point}")
print(f"实部(新x): {rotated_point.real}, 虚部(新y): {rotated_point.imag}")
# 预期:(3, 4) 逆时针旋转90度 -> (-4, 3)
四、性能优化技巧
如果你要处理大量复数运算,NumPy 是更好的选择。
import numpy as np
import cmath
# 单个复数运算
z1 = 3 + 4j
z2 = 1 - 2j
result = z1 * z2
# 批量复数运算(用 NumPy)
z_array = np.array([3+4j, 1-2j, 5+6j, -1+1j])
result_array = z_array * 2
print(result_array)
# [ 6.+8.j 2.-4.j 10.+12.j -2.+2.j]
# NumPy 的复数函数
print(np.abs(z_array)) # 模
print(np.angle(z_array)) # 幅角(弧度)
print(np.conj(z_array)) # 共轭
什么时候用原生复数,什么时候用 NumPy?
- 少数几个复数运算 → 用原生
complex - 大量复数运算(比如信号处理、图像处理)→ 用 NumPy
五、总结 checklist
写代码前,对照这个清单检查:
- [ ] 虚部系数不能省略,
1j不能写成j - [ ]
complex()解析字符串时不能有空格 - [ ] 数学函数用
cmath,不用math - [ ] 复数除法结果还是复数,别以为变成了整数
- [ ] 批量运算考虑用 NumPy
- [ ] 需要幅角时用
cmath.phase()或np.angle() - [ ] 需要极坐标时用
cmath.polar()或np.abs() + np.angle()
复数在 Python 里其实很好用,只要你避开那几个小坑。记住,j 不是变量,1j 才是。其他时候,放心大胆地算吧。
