
1. 项目概述当C遇见量子叠加作为一名在C高性能计算领域摸爬滚打了十多年的老码农我最近几年被“量子计算”这个词反复轰炸。无论是新闻里谷歌的“量子霸权”还是各种技术论坛上关于量子算法的讨论都让人感觉一个新时代要来了。但说实话对于绝大多数像我这样的经典程序员来说量子计算机的硬件离我们还很远那些神秘的“量子比特”、“叠加态”、“纠缠”听起来更像是物理学家的事情。直到我开始琢磨一个问题我们能不能用我们最熟悉的工具——经典计算机特别是C去模拟和理解这些量子概念的核心呢尤其是那个听起来最玄乎的“量子态叠加”和它带来的“量子并行性”。这个想法就是今天这个项目的由来。它不是一个能替代真实量子计算机的模拟器那需要海量的计算资源。它的目标更实际用C构建一个轻量级的、教育性质的量子态叠加模拟框架让我们能亲手“触摸”到量子并行性的计算思想。通过代码我们将看到一个处于叠加态的量子比特如何在数学上同时表示0和1以及当我们操作一个由多个叠加态量子比特组成的系统时那种指数级增长的“并行”计算潜力是如何在经典计算机上被模拟出来的。这对于理解肖尔算法、格罗弗算法等量子算法的底层逻辑有着不可替代的价值。无论你是对量子计算好奇的C开发者还是想深入理解计算本质的学生这个项目都能提供一个从代码层面切入的绝佳视角。2. 核心原理从经典比特到量子比特的数学跨越在开始敲代码之前我们必须把地基打牢。量子计算的核心抽象是量子比特它与我们熟知的经典比特有本质区别。理解这种区别是模拟工作的第一步。2.1 经典比特的确定性世界在经典计算机中一个比特Bit的状态是确定且互斥的它要么是0要么是1。我们可以用一个二维向量来形式化地表示它状态0表示为[1, 0]^T(列向量)状态1表示为[0, 1]^T这里的“T”表示转置。这种表示方法虽然看起来有点杀鸡用牛刀但它为过渡到量子比特提供了完美的数学桥梁。经典比特的所有操作与、或、非等逻辑门本质上都是在{0, 1}这个离散集合上的映射。2.2 量子比特的叠加态原理一个量子比特Qubit的状态则是一个二维复向量空间中的单位向量。这意味着它的状态|ψ⟩可以写成基态|0⟩和|1⟩的线性叠加|ψ⟩ α|0⟩ β|1⟩其中|0⟩和|1⟩是正交基向量通常对应经典的计算基态即|0⟩ [1, 0]^T,|1⟩ [0, 1]^T。α 和 β 是复数称为概率幅Probability Amplitude。最关键的限制来自量子力学的概率解释这个量子比特处于状态|0⟩的概率是|α|²处于状态|1⟩的概率是|β|²。由于总概率必须为1因此有|α|² |β|² 1。这解释了为什么状态向量必须是“单位”向量。这就是叠加态的核心在测量之前量子比特并非“既是0又是1”而是以一种由α和β描述的、同时包含两种可能性的概率幅形式存在。测量行为会迫使它“坍缩”到|0⟩或|1⟩中的一个坍缩到某个结果的概率由对应的概率幅模平方决定。注意这里最容易产生的误解是认为叠加态等于“同时存储了0和1两个值”。实际上它存储的是关于0和1的概率分布信息。你无法直接读取α和β它们是复数只能通过多次制备相同的量子比特状态并测量用统计频率来逼近|α|²和|β|²。这在我们的模拟中会通过随机数生成来体现。2.3 量子并行性的模拟思想量子并行性是量子算法加速的源泉。假设我们有一个量子函数f(x)它作用于一个量子比特。如果我们准备一个输入量子比特处于叠加态(|0⟩ |1⟩)/√2那么经过函数处理后的输出状态将包含f(0)和f(1)的信息。从某种意义上说函数f在“同一时间”计算了所有可能的经典输入0和1对应的输出。扩展到n个量子比特n个经典比特只能表示2ⁿ个状态中的一个比如0110...。而n个处于纠缠或独立叠加的量子比特其状态空间是2ⁿ个计算基态的叠加|ψ⟩ Σ_{x0}^{2ⁿ-1} c_x |x⟩ 其中Σ |c_x|² 1。一个作用于这个n量子比特系统的量子操作一个巨大的幺正矩阵理论上可以同时变换所有2ⁿ个系数{c_x}。这就是我们试图在经典计算机上模拟的“并行性”我们不会真的并行执行2ⁿ个线程而是通过线性代数运算一次性地更新这个包含2ⁿ个复数的状态向量。虽然最终提取有用信息仍需技巧如量子干涉但计算过程本身确实遍历了指数级大的状态空间。3. C模拟框架的设计与核心类实现理解了原理我们就可以着手设计C模拟框架了。我们的目标是清晰、高效且易于教学因此不会过度优化但会保证结构的正确性。3.1 状态表示复数与向量的选择首先需要选择表示复数和高维向量的工具。复数C标准库提供了std::complexT模板类完美适用于我们的需求。我们将使用std::complexdouble来保证足够的精度。状态向量一个n量子比特系统的状态由2ⁿ个复数组成。我们将使用std::vectorstd::complexdouble来表示。虽然从性能角度看对于大型模拟使用如Eigen、Blaze这样的线性代数库或自定义内存对齐数组会更优但为了代码的简洁性和可读性我们先用标准库容器。关键在于我们要始终维护状态向量的归一化条件所有概率幅模平方和为1。QubitState 类设计#include vector #include complex #include cassert #include cmath class QubitState { private: std::vectorstd::complexdouble amplitudes; // 状态向量 int num_qubits; // 量子比特数 public: // 构造函数初始化指定数量量子比特的状态默认为全|0态 explicit QubitState(int n) : num_qubits(n), amplitudes(1 n, 0.0) { if (n 0) throw std::invalid_argument(Number of qubits must be positive.); amplitudes[0] 1.0; // |00...0态的概率幅为1 } // 获取量子比特数量 int get_num_qubits() const { return num_qubits; } // 获取状态向量只读 const std::vectorstd::complexdouble get_amplitudes() const { return amplitudes; } // 获取/设置特定计算基态的概率幅 (索引x对应二进制态) std::complexdouble get_amplitude(int index) const { assert(index 0 index (1 num_qubits)); return amplitudes[index]; } void set_amplitude(int index, const std::complexdouble amp) { assert(index 0 index (1 num_qubits)); amplitudes[index] amp; } // 一个重要的工具函数检查并重新归一化状态向量防止浮点误差累积 void renormalize() { double norm 0.0; for (const auto amp : amplitudes) { norm std::norm(amp); // norm() 返回模的平方 |amp|^2 } norm std::sqrt(norm); if (std::abs(norm - 1.0) 1e-12) { // 设置一个容忍度 for (auto amp : amplitudes) { amp / norm; } } } // 测量函数模拟量子测量返回坍缩后的经典比特串并更新状态向量 // 这是体现量子随机性的关键函数 unsigned int measure() { // 1. 根据概率幅计算每个基态的概率 std::vectordouble probabilities(amplitudes.size()); std::transform(amplitudes.begin(), amplitudes.end(), probabilities.begin(), [](const std::complexdouble amp) { return std::norm(amp); }); // 2. 生成一个[0, 1)的随机数决定坍缩结果 std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution dis(0.0, 1.0); double r dis(gen); // 3. 根据概率分布选择结果 double cumulative_prob 0.0; unsigned int result 0; for (size_t i 0; i probabilities.size(); i) { cumulative_prob probabilities[i]; if (r cumulative_prob) { result static_castunsigned int(i); break; } } // 4. 更新状态向量坍缩到测量结果对应的基态 // 即将result对应的概率幅设为1其余设为0 std::fill(amplitudes.begin(), amplitudes.end(), 0.0); amplitudes[result] 1.0; return result; // 返回测量结果整数形式可转换为二进制 } };实操心得renormalize()函数至关重要。由于浮点运算的精度限制多次量子门操作后状态向量的模长可能会略微偏离1。定期或在不连续操作后调用此函数可以避免概率总和不为1导致的数值不稳定。这是经典模拟中一个非常实际的“坑”。3.2 量子门操作幺正变换的实现量子门是对量子比特状态进行操作的幺正Unitary矩阵。幺正性保证了变换前后状态向量的模长不变即总概率守恒。我们需要实现一些基本的量子门。单量子比特门作用于单个量子比特但在n量子比特系统中它会影响整个状态向量中与该比特相关的所有振幅。我们通过张量积和矩阵乘法的思想来实现。更高效的做法是直接计算门对状态向量的影响而不是构造巨大的2ⁿ x 2ⁿ矩阵。以泡利-X门量子非门和哈达玛门H门用于创建叠加态为例class QuantumGate { public: // 应用泡利-X门比特翻转到指定的量子比特上 static void apply_pauli_x(QubitState state, int target_qubit) { int n state.get_num_qubits(); assert(target_qubit 0 target_qubit n); int step 1 target_qubit; // 目标比特在索引中的“步长” int group_size 1 (target_qubit 1); auto amps const_caststd::vectorstd::complexdouble(state.get_amplitudes()); // 谨慎操作 // 遍历所有“对”并交换振幅 for (int i 0; i (1 n); i group_size) { for (int j 0; j step; j) { int idx0 i j; // 对应目标比特为0的索引 int idx1 idx0 step; // 对应目标比特为1的索引 std::swap(amps[idx0], amps[idx1]); } } } // 应用哈达玛门到指定的量子比特上 (创建叠加态) static void apply_hadamard(QubitState state, int target_qubit) { int n state.get_num_qubits(); assert(target_qubit 0 target_qubit n); int step 1 target_qubit; int group_size 1 (target_qubit 1); auto amps const_caststd::vectorstd::complexdouble(state.get_amplitudes()); const double inv_sqrt2 1.0 / std::sqrt(2.0); std::vectorstd::complexdouble new_amps(amps.size(), 0.0); // 更安全的实现先计算到新向量再替换 for (int i 0; i (1 n); i group_size) { for (int j 0; j step; j) { int idx0 i j; int idx1 idx0 step; std::complexdouble a0 amps[idx0]; std::complexdouble a1 amps[idx1]; // H|0 (|0 |1)/√2, H|1 (|0 - |1)/√2 new_amps[idx0] inv_sqrt2 * (a0 a1); new_amps[idx1] inv_sqrt2 * (a0 - a1); } } amps.swap(new_amps); // 理论上哈达玛门是幺正的保持归一化但浮点误差下可调用renormalize // state.renormalize(); } };多量子比特门以受控非门CNOT为例它是一个两量子比特门当控制比特为|1时翻转目标比特。// 应用受控非门(CNOT) static void apply_cnot(QubitState state, int control_qubit, int target_qubit) { int n state.get_num_qubits(); assert(control_qubit 0 control_qubit n); assert(target_qubit 0 target_qubit n); assert(control_qubit ! target_qubit); int control_step 1 control_qubit; int target_step 1 target_qubit; auto amps const_caststd::vectorstd::complexdouble(state.get_amplitudes()); // 遍历所有基态只对控制比特为1的组进行操作 for (int i 0; i (1 n); i) { // 检查当前基态索引i的二进制表示中控制比特是否为1 if ((i control_step) ! 0) { // 计算目标比特翻转后的索引 int j i ^ target_step; // 异或运算翻转目标比特 if (i j) { // 避免重复交换 std::swap(amps[i], amps[j]); } } } }注意事项上述门操作的实现采用了“就地更新”和基于索引模式遍历的方法避免了构造庞大的变换矩阵这是模拟少量量子比特比如20个以内的常用技巧。但当量子比特数增多时状态向量大小呈指数增长2ⁿ这种模拟方法会迅速遇到内存和计算量的瓶颈这就是经典模拟的极限也反衬出真正量子计算机的价值。4. 模拟量子并行性从概念到代码演示有了框架我们就可以通过几个具体的例子直观地展示量子叠加和并行性是如何在C模拟中体现的。4.1 案例一单量子比特叠加与测量统计让我们创建一个处于|0态的量子比特然后对其施加哈达玛门使其进入叠加态(|0⟩ |1⟩)/√2最后进行多次测量观察统计结果。#include iostream #include map #include iomanip void demo_single_qubit_superposition() { std::cout 演示1单量子比特叠加与测量 std::endl; const int NUM_TRIALS 10000; std::mapunsigned int, int result_counts; // 记录测量结果次数 for (int t 0; t NUM_TRIALS; t) { // 1. 初始化一个量子比特处于 |0 态 QubitState q(1); // 1个量子比特 // 2. 应用哈达玛门创建叠加态 (|0 |1)/√2 QuantumGate::apply_hadamard(q, 0); // 3. 测量量子比特 unsigned int measurement_result q.measure(); // 结果为0或1 // 4. 统计 result_counts[measurement_result]; } // 5. 输出统计结果 std::cout 对叠加态量子比特进行 NUM_TRIALS 次测量 std::endl; for (const auto [result, count] : result_counts) { double probability static_castdouble(count) / NUM_TRIALS; std::cout 测得 | result ⟩ 的次数: std::setw(5) count , 概率 ≈ std::fixed std::setprecision(4) probability std::endl; } // 理论上|0和|1的概率应各为0.5 std::cout 理论预测P(|0) 0.5, P(|1) 0.5 std::endl std::endl; }运行这段代码你会看到测量结果为0和1的次数大致相等各接近50%。这直观地验证了叠加态的概率解释。我们的模拟通过随机数生成器忠实地再现了量子测量的随机性。4.2 案例二量子并行性初探——同时计算函数值这是展示“量子并行性”思想的核心例子。假设我们有一个经典函数f(x): {0,1} - {0,1}。在经典计算机上我们需要调用两次函数才能得到f(0)和f(1)。在量子计算机上通过巧妙的线路设计我们可以“一次”得到包含两个结果的信息。我们使用一个简单的函数f(x) x恒等函数和f(x) 1常数1函数来演示。量子实现通常需要两个量子比特一个输入寄存器一个输出寄存器。我们使用Deutsch算法的简化版思想。// 一个“黑盒”量子门它实现了函数f(x)的量子版本。 // 对于常数函数f(x)0或1以及平衡函数f(x)x有不同的构造。 void apply_function_oracle(QubitState state, const std::string func_type) { // 假设state有两个量子比特q0是输入q1是输出初始为|0 // 量子Oracle实现映射 |x, y - |x, y ⊕ f(x)其中⊕是异或 if (func_type constant_0) { // f(x)0, 所以 |x, y - |x, y ⊕ 0 |x, y, 什么都不做 return; } else if (func_type constant_1) { // f(x)1, 所以 |x, y - |x, y ⊕ 1, 即总是翻转第二个量子比特 // 这等价于在第二个量子比特上作用X门 QuantumGate::apply_pauli_x(state, 1); } else if (func_type balanced_x) { // f(x)x, 所以 |x, y - |x, y ⊕ x // 这是一个受控非门(CNOT)以q0为控制q1为目标 QuantumGate::apply_cnot(state, 0, 1); } } void demo_quantum_parallelism_deutsch() { std::cout 演示2量子并行性简化Deutsch问题 std::endl; // 测试两种函数常数函数和平衡函数 std::vectorstd::string functions {constant_1, balanced_x}; for (const auto func : functions) { std::cout \n测试函数类型: func std::endl; // 1. 初始化两个量子比特|00 QubitState q(2); // 2. 准备输入量子比特为叠加态 QuantumGate::apply_hadamard(q, 0); // 第一个量子比特变成 (|0|1)/√2 // 第二个量子比特输出也变成|1态在标准Deutsch算法中输出初始化为|1并施加H门 // 为了简化演示我们采用另一种常见线路输出初始为|0但Oracle定义包含相位翻转 // 这里我们调整Oracle和线路以匹配我们的简单实现 QuantumGate::apply_hadamard(q, 1); // 将第二个量子比特也置于(|0-|1)/√2态这对区分函数类型有帮助 // 3. 应用函数Oracle黑盒 apply_function_oracle(q, func); // 4. 再次对输入量子比特应用哈达玛门 QuantumGate::apply_hadamard(q, 0); // 第二个量子比特我们暂时不测量 // 5. 测量第一个量子比特输入寄存器 // 在Deutsch算法中如果测得|0则函数是常数函数如果测得|1则是平衡函数。 // 注意由于我们简化了线路结果可能需要调整解释。关键在于展示操作过程。 unsigned int result q.measure(); // 测量整个系统但我们只看第一个比特 int first_qubit_result result 1; // 获取最低位第一个量子比特 std::cout 测量结果二进制低位为q0: std::bitset2(result) std::endl; std::cout 第一个量子比特(q0)的测量值: first_qubit_result std::endl; // 分析在我们的简化设置下... if (func.find(constant) ! std::string::npos) { std::cout (这是一个常数函数预期q0以高概率为0) std::endl; } else { std::cout (这是一个平衡函数预期q0以高概率为1) std::endl; } } std::cout \n说明通过一次调用量子Oracle函数我们就能获取关于函数全局性质常数或平衡的信息这体现了量子并行性的优势。经典计算机需要调用两次函数才能确定。 std::endl; }这个演示的关键在于我们只调用了一次apply_function_oracle这个操作同时处理了输入寄存器中|0⟩和|1⟩的叠加态。尽管最终我们只提取了一个比特的信息函数是常数还是平衡但这个结论的得出在概念上依赖于对两个可能输入的同时处理。这就是量子并行性在算法层面的一个缩影。4.3 案例三多量子比特叠加与状态空间爆炸让我们直观感受一下指数级的状态空间。我们初始化n个量子比特都为|0然后对每一个都施加哈达玛门这将创建一个所有计算基态均匀叠加的状态|ψ⟩ (|00...0⟩ |00...1⟩ ... |11...1⟩) / √(2ⁿ)void demo_multi_qubit_superposition(int num_qubits) { std::cout 演示3多量子比特均匀叠加态 std::endl; std::cout 量子比特数 n num_qubits std::endl; std::cout 状态向量大小 2^ num_qubits (1 num_qubits) 个复数 std::endl; if (num_qubits 5) { std::cout 注意n较大时打印全部振幅不现实。我们将检查几个关键振幅。 std::endl; } // 1. 初始化 QubitState q(num_qubits); // 2. 对每个量子比特应用哈达玛门创建均匀叠加 for (int i 0; i num_qubits; i) { QuantumGate::apply_hadamard(q, i); } // 3. 检查归一化 double norm_sq 0.0; const auto amps q.get_amplitudes(); for (const auto amp : amps) { norm_sq std::norm(amp); } std::cout 状态向量模长的平方 (应为1): norm_sq std::endl; // 4. 打印部分振幅 std::cout 部分计算基态的概率幅 (均为实数大小约为 1/√ (1 num_qubits) ): std::endl; std::cout |; for (int i 0; i num_qubits; i) std::cout 0; std::cout ⟩ : amps[0].real() std::endl; std::cout |; for (int i 0; i num_qubits-1; i) std::cout 0; std::cout 1⟩ : amps[1].real() std::endl; int last_index (1 num_qubits) - 1; std::cout |; for (int i 0; i num_qubits; i) std::cout 1; std::cout ⟩ : amps[last_index].real() std::endl; // 5. 模拟一次测量 std::cout \n模拟一次测量 std::endl; unsigned int measured q.measure(); std::cout 坍缩到的经典状态十进制: measured std::endl; std::cout 二进制表示: ; for (int i num_qubits - 1; i 0; --i) { std::cout ((measured i) 1); } std::cout std::endl; // 测量后状态向量坍缩到该基态 std::cout 测量后状态向量中 | std::bitset8(measured).to_string().substr(8-num_qubits) ⟩ 的振幅为: q.get_amplitude(measured) std::endl; }运行demo_multi_qubit_superposition(3)和demo_multi_qubit_superposition(10)。你会看到对于3个量子比特状态向量有8个振幅每个大约为0.3536 (1/√8)。对于10个量子比特状态向量有1024个振幅。这个指数增长就是量子并行性威力的来源也是经典模拟的噩梦。一个50量子比特的系统其状态向量需要2⁵⁰ ≈ 1.125千万亿个复数来存储这远远超出了任何经典计算机的内存。5. 性能优化、局限性与扩展方向我们的模拟框架是清晰但朴素的。对于严肃的研究或教学我们需要讨论其局限性和可能的优化。5.1 经典模拟的固有局限指数级资源消耗如前所述n个量子比特需要O(2ⁿ)的内存来存储状态向量。这使得模拟超过50个量子比特的系统在现有经典计算机上完全不现实。计算复杂度即使应用一个只作用于单个量子比特的门在最朴素的实现下也需要遍历和更新整个2ⁿ维向量。虽然我们使用了基于索引模式的高效算法但复杂度仍然是O(2ⁿ)。无法模拟真正的量子效应我们的模拟基于状态向量和幺正演化这本身是量子力学的一种表述薛定谔绘景。但对于噪声、退相干、特定的量子硬件错误等需要更复杂的模型如密度矩阵、量子轨迹等计算开销更大。这恰恰说明了为什么我们需要真正的量子计算机它们天然地以指数级压缩的方式表示和操作这些叠加态。5.2 C模拟的优化技巧尽管有根本性局限对于中小规模模拟n30我们仍可以大幅优化使用高效线性代数库用Eigen、Intel MKL或CUDA对于GPU来执行向量和矩阵运算能极大提升性能。特别是利用SIMD指令和并行计算。稀疏性和压缩表示许多量子算法产生的状态向量并非完全稠密。可以利用稀疏数据结构来存储非零振幅。对于具有特定结构如矩阵乘积态MPS的量子态可以使用张量网络方法进行压缩表示这在模拟一维量子系统时特别有效。即时编译(JIT)与专用指令集像Qulacs、Qiskit AerC后端这样的高性能模拟器会为特定的量子线路生成优化的机器码避免解释执行的开销。分布式计算将巨大的状态向量分割到多台机器的内存中使用MPI进行通信。这是模拟超过30个量子比特的常用方法但通信开销巨大。5.3 项目扩展方向这个基础框架可以扩展到很多有趣的方向实现更多量子门和算法实现T门、S门、Swap门等进而组合实现量子傅里叶变换QFT、以及完整的格罗弗搜索算法和肖尔整数分解算法的模拟。这将是对量子算法最深入的学习。可视化工具使用如SFML、SDL或WebAssembly前端可视化布洛赫球上单个量子比特的演化或者绘制多量子比特系统的概率分布直方图。量子电路解析器设计一个简单的领域特定语言DSL或读取QASM量子汇编语言文件将文本描述的量子电路自动转换为我们的C模拟操作序列。噪声模拟引入量子门错误、测量错误和退相干模型振幅阻尼、相位阻尼通道模拟含噪声的量子计算这对于理解量子纠错至关重要。与真实量子后端对接将模拟框架的API设计成与IBM Qiskit、Amazon Braket等云量子计算平台的接口类似。这样同一段高级量子程序既可以本地模拟也可以提交到真实的量子处理器上运行方便对比结果。6. 常见问题与调试心得在开发和运行这类模拟器时我踩过不少坑这里总结一下测量结果总是确定性的没有随机性原因随机数生成器没有正确初始化或种子固定。确保在每次实验或每次测量时使用不同的随机种子如std::random_device。检查在measure()函数中确认随机数生成逻辑正确且概率分布probabilities计算无误。状态向量在多次操作后“不归一化”了概率和远大于或小于1原因浮点运算累积误差。这是数值计算中的常见问题。解决定期调用renormalize()函数。更稳健的做法是在实现每个量子门操作时确保其幺正性在浮点误差范围内得到保持。对于复杂的自定义门可以在应用后强制归一化。应用多量子比特门如CNOT后结果看起来不对原因最可能是量子比特索引顺序混淆。在状态向量amplitudes[index]中index的二进制表示通常约定最低位LSB对应第0个量子比特q0。但在实现门操作时控制/目标比特的索引与index中位的对应关系必须一致。调试用一个小例子如2个量子比特手动计算。初始化|00,|01,|10,|11分别应用门打印操作前后的状态向量与手工计算或已知正确结果对比。模拟速度太慢稍微增加量子比特数就卡死原因这是预期之中的因为复杂度是O(2ⁿ)。这是经典模拟的根本限制。优化使用-O3优化编译。检查循环确保没有不必要的拷贝或重复计算。对于固定线路可以考虑预计算整个变换矩阵如果n很小或者使用更高级的模拟技术如基于决策图的模拟。接受现实模拟20个以上量子比特的全状态向量对普通电脑已是挑战。如何验证我的模拟是正确的单元测试为每个量子门写测试。例如对H门验证H|0和H|1产生的状态向量是否正确且测量统计符合50%/50%。算法验证实现已知的简单量子算法如Deutsch-Jozsa算法或贝尔态制备检查其输出是否符合理论预测。交叉验证用你的模拟器运行一个简单电路再在IBM Quantum Composer或Quirk等在线模拟器上运行同样的电路对比最终状态或测量统计结果。这个用C模拟量子态叠加的项目就像是在经典计算的土地上用我们熟悉的砖瓦搭建一座通往量子世界概念的桥梁。它无法带你到达彼岸但能让你清晰地看到对岸的风景理解那座桥量子计算机为何非造不可。通过亲手实现这些代码你会对“叠加”、“纠缠”、“并行性”这些词汇有刻骨铭心的理解而不再觉得它们遥不可及。当未来某天你真正需要为量子算法编写代码时这段在经典世界中模拟量子的经历将成为你最宝贵的直觉来源。