C++ 标准库 <complex>(完整教程)

C++ 标准库 :轻松玩转复数运算

你有没有在数学课上见过虚数单位 i?它满足 i² = -1,这在实数范围内是“不可能”的。但正是这个“不可能”,让复数在信号处理、电磁场分析、量子力学等领域大放异彩。而在 C++ 中,我们无需手动实现复数的加减乘除,因为标准库已经为我们准备好了强大的工具——<complex>

<complex> 是 C++ 标准库中的一个头文件,它提供了对复数类型的支持,让开发者能够以非常直观的方式操作复数。无论是做图像处理中的傅里叶变换,还是模拟电路中的阻抗计算,<complex> 都能让你事半功倍。

想象一下:你正在写一个音频滤波器程序,需要对频域数据进行相位调整。这时候,用实数根本无法表达相位信息,但复数可以。<complex> 就像一个专业的“复数管家”,帮你处理所有复杂的运算细节。


复数类型与基本语法

在 C++ 中,<complex> 提供了三种主要的复数类型:

  • std::complex<float>:使用 float 类型表示实部和虚部
  • std::complex<double>:使用 double 类型,精度更高(默认选择)
  • std::complex<long double>:使用 long double,适合极高精度需求

这些类型都定义在 std:: 命名空间中,使用时需要包含头文件 <complex>

#include <iostream>
#include <complex>

int main() {
    // 创建一个复数:3 + 4i
    std::complex<double> z1(3.0, 4.0);

    // 也可以只给实部,虚部默认为 0
    std::complex<double> z2(5.0);

    // 输出复数,会自动显示为 (实部, 虚部)
    std::cout << "z1 = " << z1 << std::endl;  // 输出: (3, 4)
    std::cout << "z2 = " << z2 << std::endl;  // 输出: (5, 0)

    return 0;
}

💡 小贴士std::complex 的构造函数接受两个参数:第一个是实部,第二个是虚部。如果只传一个参数,虚部默认为 0。


复数的基本运算

<complex> 支持所有常见的复数运算,包括加法、减法、乘法和除法。这些运算都遵循复数的数学规则。

#include <iostream>
#include <complex>

int main() {
    std::complex<double> a(1.0, 2.0);  // 1 + 2i
    std::complex<double> b(3.0, -1.0); // 3 - i

    // 加法:(1+2i) + (3-i) = 4 + i
    std::complex<double> sum = a + b;
    std::cout << "a + b = " << sum << std::endl;  // 输出: (4, 1)

    // 减法:(1+2i) - (3-i) = -2 + 3i
    std::complex<double> diff = a - b;
    std::cout << "a - b = " << diff << std::endl;  // 输出: (-2, 3)

    // 乘法:(1+2i) * (3-i) = 3 - i + 6i - 2i² = 3 + 5i + 2 = 5 + 5i
    std::complex<double> product = a * b;
    std::cout << "a * b = " << product << std::endl;  // 输出: (5, 5)

    // 除法:(1+2i) / (3-i) = (1+2i)(3+i) / (9+1) = (3+i+6i+2i²)/10 = (1+7i)/10
    std::complex<double> quotient = a / b;
    std::cout << "a / b = " << quotient << std::endl;  // 输出: (0.1, 0.7)

    return 0;
}

关键点:复数乘法中会涉及 i² = -1,<complex> 内部会自动处理这个规则,无需手动计算。


复数的几何意义:模与幅角

复数在复平面上可以看作一个点,或者一条从原点出发的向量。它的“长度”叫模(magnitude),与正实轴的夹角叫幅角(argument)。

  • 模:|z| = √(a² + b²)
  • 幅角:θ = atan2(b, a),注意使用 atan2 而不是 atan,因为它能正确判断象限
#include <iostream>
#include <complex>
#include <cmath>

int main() {
    std::complex<double> z(3.0, 4.0);  // 3 + 4i

    // 计算模
    double magnitude = std::abs(z);
    std::cout << "|z| = " << magnitude << std::endl;  // 输出: 5

    // 计算幅角(单位:弧度)
    double angle = std::arg(z);
    std::cout << "arg(z) = " << angle << " 弧度" << std::endl;  // 输出: ~0.927 弧度

    // 也可以转换为角度
    double angle_deg = angle * 180.0 / M_PI;
    std::cout << "arg(z) = " << angle_deg << " 度" << std::endl;  // 输出: ~53.13 度

    return 0;
}

🌟 比喻:你可以把复数想象成一个指南针。实部是东西方向,虚部是南北方向。模就是你走的总距离,幅角就是你指的方向。


常用数学函数支持

<complex> 不仅支持基本运算,还提供了完整的数学函数库,比如幂函数、指数、对数、三角函数等。

这些函数都重载了复数版本,可以直接用于 std::complex 类型。

#include <iostream>
#include <complex>
#include <cmath>

int main() {
    std::complex<double> z(0.0, 1.0);  // i

    // 欧拉公式:e^(iθ) = cosθ + i sinθ
    // 当 θ = π 时,e^(iπ) = -1
    std::complex<double> exp_result = std::exp(z * std::acos(-1.0));  // e^(iπ)
    std::cout << "e^(iπ) = " << exp_result << std::endl;  // 输出: (-1, 0)

    // 计算 i 的平方
    std::complex<double> square = z * z;
    std::cout << "i^2 = " << square << std::endl;  // 输出: (-1, 0)

    // 计算 ln(i)
    std::complex<double> log_result = std::log(z);
    std::cout << "ln(i) = " << log_result << std::endl;  // 输出: (0, 1.5708)

    // 三角函数:sin(i)
    std::complex<double> sin_result = std::sin(z);
    std::cout << "sin(i) = " << sin_result << std::endl;  // 输出: (0, 1.1752)

    return 0;
}

⚠️ 注意:复数的对数和三角函数结果也是复数,不要期望得到“纯实数”结果。


实际应用:信号相位偏移

让我们看一个真实场景:音频信号处理中,经常需要对某个频率分量进行相位偏移。这就可以用复数来实现。

假设我们有一个正弦波信号,其频率为 100 Hz,采样率为 1000 Hz。我们希望将它的相位向前移动 90 度(即 π/2 弧度)。

#include <iostream>
#include <complex>
#include <vector>
#include <cmath>

int main() {
    const int N = 100;  // 采样点数
    const double fs = 1000.0;  // 采样频率
    const double f = 100.0;    // 信号频率
    const double phase_shift = M_PI / 2.0;  // 90 度相位偏移

    std::vector<std::complex<double>> signal(N);
    std::vector<std::complex<double>> shifted_signal(N);

    // 生成原始信号:sin(2πft)
    for (int n = 0; n < N; ++n) {
        double t = n / fs;
        signal[n] = std::complex<double>(0.0, 1.0) * std::sin(2.0 * M_PI * f * t);
    }

    // 用复数相位旋转实现相位偏移
    std::complex<double> phase_factor = std::polar(1.0, phase_shift);  // e^(iθ)

    for (int n = 0; n < N; ++n) {
        shifted_signal[n] = signal[n] * phase_factor;
    }

    // 输出前几个点验证
    std::cout << "原始信号 (前 5 个):" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << "signal[" << i << "] = " << signal[i] << std::endl;
    }

    std::cout << "\n相位偏移后 (前 5 个):" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << "shifted[" << i << "] = " << shifted_signal[i] << std::endl;
    }

    return 0;
}

亮点std::polar(1.0, phase_shift) 生成一个单位模、指定幅角的复数,相当于旋转因子。用它乘原信号,就完成了相位偏移。


总结与建议

C++ 标准库 <complex> 是一个功能强大且易于使用的工具,它让复数运算变得简洁、安全、高效。无论你是做科学计算、工程模拟,还是信号处理,掌握它都能显著提升开发效率。

建议你:

  • 优先使用 std::complex<double>,精度足够且性能好
  • 利用 std::absstd::arg 快速获取复数的模和幅角
  • 使用 std::polar 构造旋转因子,简化相位处理
  • 不要手动实现复数运算,<complex> 已经帮你写好了所有边界情况

复数不是“虚幻”的,它在现实世界中有着广泛的应用。而 C++ 的 <complex>,就是连接数学与编程的桥梁。

当你在代码中写下 std::complex<double> z(1.0, 2.0); 时,你不仅是在写一行代码,更是在与数学之美对话。