Lesson 70: 量子比特门电路
练习任务
难度:中
用 C 语言实现一个单量子比特模拟器,模拟量子态在 Hadamard 门(H)、Pauli-X 门(X)和 Pauli-Z 门(Z)作用下的演化,并最终执行 100 次量子测量。需要完成 6 个核心函数:
complex_mult()— 复数乘法:(a+bi)(c+di) = (ac−bd) + (ad+bc)icomplex_add()— 复数加法:(a+bi)+(c+di) = (a+c)+(b+d)iapply_gate()— 将 2×2 量子门矩阵作用于量子态向量print_state()— 以 Dirac 符符号格式输出量子态measure()— 模拟一次投影测量,基于 LCG 伪随机数main()— 驱动模拟流程:初始化 |0⟩ → H → X → Z → H → 100 次测量
门矩阵常量(H、X、Z)和 Complex 结构体已由框架提供。
cd exercises/70_qubit-gate-simulator
make test验证:编译运行后与 expected_output.txt 逐行 diff 比较,完全匹配则通过。
提示:
apply_gate中必须使用临时变量保存new0和new1,计算完两个分量后再写回。若先写回state[0]再计算state[1],读取到的state[0]已是新值而非旧值。complex_mult的虚部是 ad+bc,不是减号—这是最常见的符号错误。
核心知识点
- 量子比特的数学表示 —
|ψ⟩ = α|0⟩ + β|1⟩,α、β 是复数振幅,满足归一化条件|α|² + |β|² = 1;Bloch 球将任意纯态映射到单位球面 - 复数结构体与运算 —
Complex {double real, imag},复乘公式(ac−bd) + (ad+bc)i,复加直接分量相加 - 量子门作为酉矩阵 — 量子门是 2×2 酉矩阵(
U†U = I),保证可逆且保持归一化;Hadamard 门制造叠加态、Pauli-X 翻转比特、Pauli-Z 翻转相位 - apply_gate 的实现细节 — 矩阵-向量乘法
G|ψ⟩,必须用临时变量避免"写后读"数据竞争;C 语言中 2D 数组函数参数传递 - 量子测量与 Born 规则 — 以概率
|α|²坍缩到 |0⟩、以概率|β|²坍缩到 |1⟩;LCG 伪随机模拟可复现的测量结果 - 电路演化追踪 — H→X→Z→H 的完整状态追踪:|0⟩→|+⟩→|+⟩→|−⟩→|1⟩,理解每一门的 Bloch 球旋转
- 酉矩阵恒等式 — H²=I、X²=I、Z²=I、HXH=Z、HZH=X,Hadamard 门作为 Pauli 矩阵基底转换器
代码框架
#include <math.h>
#include <stdio.h>
/* Complex number type (provided) */
typedef struct {
double real;
double imag;
} Complex;
/* 1/√2 for Hadamard gate */
#define SQRT2_2 0.7071067811865476
/* Gate matrices — provided as constants */
static const Complex H[2][2] = {
{{SQRT2_2, 0}, {SQRT2_2, 0}},
{{SQRT2_2, 0}, {-SQRT2_2, 0}},
};
static const Complex X[2][2] = {
{{0, 0}, {1, 0}},
{{1, 0}, {0, 0}},
};
static const Complex Z[2][2] = {
{{1, 0}, {0, 0}},
{{0, 0}, {-1, 0}},
};
/* ─── TODO 1: complex_mult ───
* (a+bi)(c+di) = (ac-bd) + (ad+bc)i */
static Complex complex_mult(Complex a, Complex b) {
// ① result.real = a.real*b.real - a.imag*b.imag
// ② result.imag = a.real*b.imag + a.imag*b.real
// ③ return result
}
/* ─── TODO 2: complex_add ───
* (a+bi)+(c+di) = (a+c)+(b+d)i */
static Complex complex_add(Complex a, Complex b) {
// ① result.real = a.real + b.real
// ② result.imag = a.imag + b.imag
// ③ return result
}
/* ─── TODO 3: apply_gate ───
* |ψ'⟩ = G|ψ⟩: new_state = G × state
* 关键:必须用临时变量! */
static void apply_gate(const Complex gate[2][2], Complex state[2]) {
// ① 用 complex_mult + complex_add 计算:
// new0 = gate[0][0]*state[0] + gate[0][1]*state[1]
// ② 计算 new1 = gate[1][0]*state[0] + gate[1][1]*state[1]
// ③ 将 new0、new1 写回 state[0]、state[1]
}
/* ─── TODO 4: print_state ───
* 格式必须精确匹配 expected_output.txt */
static void print_state(const char *label, const Complex state[2]) {
// ① printf("%s\n", label)
// ② printf("( %+f %+fi )|0⟩ + ( %+f %+fi )|1⟩\n",
// state[0].real, state[0].imag,
// state[1].real, state[1].imag)
// ③ printf("\n")
}
/* ─── TODO 5: measure ───
* Born rule: P(|0⟩) = |α|², P(|1⟩) = |β|²
* LCG: seed = (1103515245u * seed + 12345u) & 0x7FFFFFFFu */
static int measure(const Complex state[2], unsigned int *seed) {
// ① double prob0 = state[0].real*state[0].real
// + state[0].imag*state[0].imag
// ② *seed = (1103515245u * (*seed) + 12345u) & 0x7FFFFFFFu
// ③ double r = (double)(*seed) / (double)0x80000000u
// ④ if (r < prob0) return 0; else return 1
}
/* ─── TODO 6: main ───
* 流程: |0⟩ → H → X → Z → H → 测量 100 次 */
int main(void) {
// ① 初始化: state[0] = {1.0, 0.0}, state[1] = {0.0, 0.0}
// ② print_state("Initial state |0⟩:", state)
// ③ apply_gate(H, state) → print_state
// ④ apply_gate(X, state) → print_state
// ⑤ apply_gate(Z, state) → print_state
// ⑥ apply_gate(H, state) → print_state
// ⑦ int counts[2] = {0,0}; unsigned int seed = 42u
// ⑧ for 100 次: { o = measure(state, &seed); counts[o]++; }
// ⑨ printf 测量统计: |0⟩: %d, |1⟩: %d (含百分比)
// ⑩ return 0
}阅读骨架后,尝试自己填充 // ① 到 // ⑩ 标记的部分。核心挑战在于:complex_mult 的虚部符号是加还是减?apply_gate 为什么必须用临时变量?measure 中 0x80000000u 为什么不能写成 0x80000000?最终 H→X→Z→H 序列的净效果是什么?
TIP
先不要往下翻看参考解答。拿纸笔手动计算 H·Z·X·H|0⟩ 的矩阵-向量乘积每一步——|0⟩ → |+⟩ → |+⟩ → |−⟩ → |1⟩。留意每一步 α 和 β 的实部、虚部变化,以及测量概率的演变。
深度讲解
1. 量子比特的数学表示——从经典比特到叠加态
1.1 经典比特 vs 量子比特
经典比特: 量子比特:
┌───┐ ┌─────────────────┐
│ 0 │ 或 │ 1 │ │ α|0⟩ + β|1⟩ │
└───┘ └───┘ │ |α|² + |β|² = 1 │
└─────────────────┘
只能处于两个确定状态之一 可以处于叠加态 (Superposition)量子比特(qubit)是量子计算的基本信息单元。与经典比特只能取 0 或 1 不同,量子比特可以同时处于 |0⟩ 和 |1⟩ 的线性叠加态:
/* 单量子比特状态:二维复向量 */
Complex state[2];
/* |0⟩ 基态:state[0] = 1, state[1] = 0 */
state[0] = (Complex){1.0, 0.0};
state[1] = (Complex){0.0, 0.0};
/* |1⟩ 基态:state[0] = 0, state[1] = 1 */
state[0] = (Complex){0.0, 0.0};
state[1] = (Complex){1.0, 0.0};
/* |+⟩ 叠加态 (均匀叠加):(|0⟩+|1⟩)/√2 */
state[0] = (Complex){0.707107, 0.0};
state[1] = (Complex){0.707107, 0.0};状态向量的完整表示:
|ψ⟩ = α|0⟩ + β|1⟩ = ┌ α ┐
└ β ┘
α = state[0].real + i·state[0].imag
β = state[1].real + i·state[1].imag
归一化条件: |α|² + |β|² = α.real² + α.imag² + β.real² + β.imag² = 11.2 Dirac 符号(Bra-Ket Notation)
| 符 符号 | 含义 | 向量表示 |
|---|---|---|
| ` | 0⟩` | 计算基态 0 |
| ` | 1⟩` | 计算基态 1 |
| ` | ψ⟩` | 任意量子态 |
| `⟨ψ | ` | 对偶向量 |
| `⟨φ | ψ⟩` | 内积 |
NOTE
"ket" |ψ⟩ 是列向量,"bra" ⟨ψ| 是行向量(共轭转置)。这个记法源自物理学家 Dirac,命名取自 bracket(括号)的 split——bra + c + ket = bracket。
1.3 Bloch 球——单量子比特的几何图像
任何单量子比特的纯态都可以映射到单位球面上的一点:
|0⟩ (北极)
▲
/│\
/ │ \
/ │ \
/ ● \ ← 任意状态 |ψ⟩
/ │ \
/ │ \
/ │ \
└───────┼───────┘
│
▼
|1⟩ (南极)
|ψ⟩ = cos(θ/2)|0⟩ + e^(iφ) sin(θ/2)|1⟩
θ ∈ [0, π] 极角 — 决定 |0⟩ 和 |1⟩ 的概率分布
φ ∈ [0, 2π) 方位角 — 决定相对相位| 状 状态 | Bloch 球位置 | θ | φ | α | β |
|---|---|---|---|---|---|
| ` | 0⟩` | 北极 | 0 | — | 1 |
| ` | 1⟩` | 南极 | π | — | 0 |
| ` | +⟩` | X 轴正方向 | π/2 | 0 | 1/√2 |
| ` | −⟩` | X 轴负方向 | π/2 | π | 1/√2 |
| ` | +i⟩` | Y 轴正方向 | π/2 | π/2 | 1/√2 |
TIP
Bloch 球可视化单量子比特的最强力工具。量子门在 Bloch 球上对应旋转——球面上的点绕某轴旋转一定角度。六种重要旋转轴:X、Y、Z、及其组合。
2. 复数运算的 C 语言实现——complex_mult 与 complex_add
2.1 Complex 结构体设计
/* 复数 a+bi:用两个 double 字段表示 */
typedef struct {
double real; /* 实部 a */
double imag; /* 虚部 b */
} Complex;
/* 示例值:
* (Complex){1.0, 2.0} → 1 + 2i
* (Complex){0.0, 1.0} → i
* (Complex){SQRT2_2, 0.0} → 1/√2 + 0i
*/WARNING
C99/C11 标准库 <complex.h> 提供了原生复数类型 double complex。但本题手动实现 Complex 结构体是为了深入理解复数运算的底层——知道 (a+bi)(c+di) 展开后实部和虚部各自是什么。
2.2 复数乘法——(ac−bd) + (ad+bc)i
/* 复数乘法公式推导:
*
* (a + bi)(c + di)
* = a·c + a·di + bi·c + bi·di
* = ac + adi + bci + bd·i²
* = ac + adi + bci - bd ← i² = -1
* = (ac - bd) + (ad + bc)i
*/
Complex complex_mult(Complex a, Complex b) {
Complex result;
result.real = a.real * b.real - a.imag * b.imag; /* ac - bd */
result.imag = a.real * b.imag + a.imag * b.real; /* ad + bc */
return result;
}
/* 验证: (1+2i) * (3+4i) = (3-8) + (4+6)i = -5 + 10i */
// complex_mult((Complex){1,2}, (Complex){3,4}) → real=-5, imag=10 ✓CAUTION
最常见的符号错误:把 result.imag = a.real*b.imag + a.imag*b.real 写成减号 a.real*b.imag - a.imag*b.real。复乘的虚部是 ad + bc,不是减号!这个错误会导致最终状态演化全错。
2.3 复数加法——直接分量相加
/* 复数加法: (a+bi) + (c+di) = (a+c) + (b+d)i */
Complex complex_add(Complex a, Complex b) {
Complex result;
result.real = a.real + b.real;
result.imag = a.imag + b.imag;
return result;
}
/* 验证: (1+2i) + (3+4i) = 4 + 6i */
// complex_add((Complex){1,2}, (Complex){3,4}) → real=4, imag=6 ✓3. 量子门——酉矩阵的物理意义
3.1 量子门 = 2×2 酉矩阵
量子门用 2×2 酉矩阵(unitary matrix)表示。酉矩阵满足 U†U = I(共轭转置等于逆),这保证:
- 量子操作可逆(有逆矩阵)
- 量子操作保持归一化(|α|²+|β|²=1 在执行门后仍成立)
门作用: |ψ'⟩ = G |ψ⟩
┌ g₀₀ g₀₁ ┐ ┌ α ┐ ┌ g₀₀·α + g₀₁·β ┐
|ψ'⟩ = │ g₁₀ g₁₁ │ × │ β │ = │ g₁₀·α + g₁₁·β │
└ ┘ └ ┘ └ ┘3.2 本题使用的三个量子门
Hadamard 门(H)— 制造叠加态
/*
* H = 1/√2 [[1, 1],
* [1, -1]]
*
* 作用:
* H|0⟩ = (|0⟩+|1⟩)/√2 = |+⟩ ← 确定态 → 叠加态
* H|1⟩ = (|0⟩−|1⟩)/√2 = |−⟩
*
* 性质:
* H² = I (自逆——施加两次回到原态)
* H|+⟩ = |0⟩, H|−⟩ = |1⟩ ← 叠加态 → 确定态
* Bloch 球: 绕 (X+Z)/√2 轴旋转 π
*
* H 是 QFT (量子傅里叶变换) 的单比特特例
*/
static const Complex H[2][2] = {
{{0.707107, 0}, { 0.707107, 0}},
{{0.707107, 0}, {-0.707107, 0}},
};Pauli-X 门(X)— 量子 NOT 门
/*
* X = [[0, 1],
* [1, 0]]
*
* 作用:
* X|0⟩ = |1⟩
* X|1⟩ = |0⟩
*
* 性质:
* X² = I (施加两次回到原态)
* Bloch 球: 绕 X 轴旋转 π (180°)
*
* X 是量子计算中的 NOT 门——但,它可以作用于叠加态!
*/
static const Complex X[2][2] = {
{{0, 0}, {1, 0}},
{{1, 0}, {0, 0}},
};Pauli-Z 门(Z)— 相位翻转
/*
* Z = [[1, 0],
* [0, -1]]
*
* 作用:
* Z|0⟩ = |0⟩ ← |0⟩ 不受影响
* Z|1⟩ = -|1⟩ ← |1⟩ 相位翻转 (乘以 -1)
*
* 性质:
* Z² = I
* Bloch 球: 绕 Z 轴旋转 π (180°)
*
* 相位翻转不改变测量概率 (|−β|² = |β|²),但在干涉效应中至关重要
*/
static const Complex Z[2][2] = {
{{1, 0}, { 0, 0}},
{{0, 0}, {-1, 0}},
};3.3 量子门的通用性质
酉矩阵的验证——以 H 为例:
H = 1/√2 [[1, 1], H† = 1/√2 [[1, 1], (共轭转置)
[1, -1]] [1, -1]]
H†·H = 1/2 [[1, 1], [[1, 1], = 1/2 [[1·1+1·1, 1·1+1·(−1)],
[1, -1]] [1, -1]] [1·1+(−1)·1, 1·1+(−1)·(−1)]]
= 1/2 [[2, 0], = [[1, 0], = I ✓
[0, 2]] [0, 1]]IMPORTANT
量子门的线性性:U(α|0⟩+β|1⟩) = αU|0⟩ + βU|1⟩。这意味着我们只需知道门对基态的作用,就能推导出对任意叠加态的作用。这是量子计算简洁描述的根本原因。
4. apply_gate——矩阵-向量乘法的正确实现
4.1 核心算法
|ψ'⟩ = G|ψ⟩
[α'] [G₀₀ G₀₁] [α] [G₀₀·α + G₀₁·β]
[β'] = [G₁₀ G₁₁] × [β] = [G₁₀·α + G₁₁·β]
每个乘法是 complex_mult,每个加法是 complex_add4.2 临时变量陷阱——"写后读"数据竞争
/* ✓ 正确实现——使用临时变量 */
static void apply_gate(const Complex gate[2][2], Complex state[2]) {
Complex new0 = complex_add(
complex_mult(gate[0][0], state[0]),
complex_mult(gate[0][1], state[1])
);
Complex new1 = complex_add(
complex_mult(gate[1][0], state[0]),
complex_mult(gate[1][1], state[1])
);
state[0] = new0;
state[1] = new1;
}/* ✗ 错误实现——state[0] 被提前覆盖 */
static void apply_gate_wrong(const Complex gate[2][2], Complex state[2]) {
/* 计算 new0 并写回 state[0] */
state[0] = complex_add(
complex_mult(gate[0][0], state[0]), /* ← 读旧 state[0] */
complex_mult(gate[0][1], state[1])
);
/* 此时 state[0] 已是新值! */
/* 接下来计算 new1 时读到的 state[0] 是错的! */
state[1] = complex_add(
complex_mult(gate[1][0], state[0]), /* ← 错误!读出的是新值 */
complex_mult(gate[1][1], state[1])
);
}错误追踪——以 H 门作用于 |0⟩ = [1, 0] 为例:
正确计算:
new0 = 0.707·1 + 0.707·0 = 0.707
new1 = 0.707·1 + (−0.707)·0 = 0.707
state = [0.707, 0.707] ✓ (|+⟩ 态)
错误计算:
state[0] = 0.707·1 + 0.707·0 = 0.707 ← 写回 state[0]
state[1] = 0.707·0.707 + (−0.707)·0 = 0.5 ← 读到的 state[0]=0.707 而不是 1!
state = [0.707, 0.5] ✗ (错误状态!)CAUTION
这是单线程程序中的数据竞争——不是多线程导致的,而是同一个表达式求值顺序导致的。计算 new1 依赖原始的 state[0],但 state[0] 已被覆盖。这个 bug 极其隐蔽——程序能编译、能运行,但结果悄悄错了。
4.3 2D 数组作为函数参数
/* 门矩阵作为 const Complex gate[2][2] 传入
*
* C 中二维数组参数的写法:
* void f(const Complex gate[2][2], Complex state[2])
* ^^^^^ ^^^^^
* gate[行][列] → 行数 2 可省略 数组大小
*
* 访问方式: gate[row][column]
* gate[0][0] → H[0][0] = {0.707107, 0}
* gate[0][1] → H[0][1] = {0.707107, 0}
* gate[1][0] → H[1][0] = {0.707107, 0}
* gate[1][1] → H[1][1] = {-0.707107, 0}
*/5. 量子测量——Born 规则与 LCG 随机模拟
5.1 Born 规则
量子测量是概率性的——这是量子力学与经典物理的根本区别:
测量 |ψ⟩ = α|0⟩ + β|1⟩:
概率 P(|0⟩) = |α|² = α.real² + α.imag²
概率 P(|1⟩) = |β|² = β.real² + β.imag²
─────────────────────────────────
总和 = |α|² + |β|² = 1 (归一化保证)
测量后: 状态坍缩 (collapse) 到测量结果对应的基态
- 得 |0⟩ → 之后测量必得 |0⟩
- 得 |1⟩ → 之后测量必得 |1⟩
叠加信息永久丢失——测量是破坏性的、不可逆的!5.2 LCG 伪随机数——可复现的随机性
本题使用线性同余生成器(Linear Congruential Generator, LCG)模拟测量中的随机性:
/*
* LCG 参数 (glibc rand() 经典参数):
* multiplier = 1103515245
* increment = 12345
* modulus = 2³¹ = 0x80000000u
*
* 递推公式:
* seed_{n+1} = (1103515245 × seed_n + 12345) mod 2³¹
*
* 归一化到 [0, 1):
* r = seed / 2³¹
*/
/* measure 函数的完整 LCG 使用 */
static int measure(const Complex state[2], unsigned int *seed) {
double prob0 = state[0].real * state[0].real
+ state[0].imag * state[0].imag;
/* 推进 LCG */
*seed = (1103515245u * (*seed) + 12345u) & 0x7FFFFFFFu;
/* 归一化到 [0, 1) */
double r = (double)(*seed) / (double)0x80000000u;
/* Born 规则 */
if (r < prob0) return 0; /* 坍缩到 |0⟩ */
else return 1; /* 坍缩到 |1⟩ */
}CAUTION
0x80000000 作为 int 是 −2147483648(在有符号 32 位溢出),是未定义行为。必须加 u 后缀写 0x80000000u 确保是无符号类型。同理 0x7FFFFFFFu 确保高位截断正确。
5.3 为什么用 LCG 而不是 rand()?
rand() 的实现在不同平台可能不同:
- glibc (Linux): 一种 LCG
- musl (Alpine): 另一种算法
- MSVC (Windows): 又一种算法
→ 同一 seed 在不同平台产生不同序列
→ expected_output.txt 无法精确预测
硬编码 LCG 的好处:
- 参数固定 (1103515245, 12345, 2³¹)
- seed=42u 产生完全相同序列
- 所有平台一致 → diff 测试可精确预测
- 这也是为什么 seed 初始值必须是 42u5.4 本题测量结果的确定性
本题最终状态 |ψ₄⟩ = |1⟩ = [0, 1]ᵀ
所以:
prob0 = 0² + 0² = 0.0
r (0 ≤ r < 1) 永远 ≥ 0.0
→ r < prob0 永远不成立
→ 100 次测量全部返回 |1⟩
这不是"运气好",而是确定性的——
最终状态是纯 |1⟩,不是叠加态。6. 电路演化完整追踪——H → X → Z → H
6.1 逐步演化
本题模拟的量子电路:
|0⟩ ──[H]──[X]──[Z]──[H]── 测量初始状态 |ψ₀⟩ = |0⟩:
|ψ₀⟩ = 1·|0⟩ + 0·|1⟩ = [1, 0]ᵀ
α = 1+0i, β = 0+0i
P(|0⟩) = 1² = 1.0 (100%), P(|1⟩) = 0² = 0.0 (0%)
Bloch 球: |0⟩ = 北极,θ=0Step 1 — H 门(产生叠加态):
|ψ₁⟩ = H|0⟩ = 1/√2 (|0⟩ + |1⟩) = |+⟩
= [1/√2, 1/√2]ᵀ
α = 1/√2 ≈ 0.707107, β = 1/√2 ≈ 0.707107
P(|0⟩) = P(|1⟩) = 0.5 (各 50%)
Bloch 球: X 轴正方向,θ=π/2, φ=0Step 2 — X 门(交换振幅):
|ψ₂⟩ = X|ψ₁⟩ = X[1/√2, 1/√2]ᵀ = [1/√2, 1/√2]ᵀ = |+⟩
因为 α=β,交换后不变!
|+⟩ 是 X 门的本征态(eigenstate),本征值为 +1.Step 3 — Z 门(翻转相位):
|ψ₃⟩ = Z|ψ₂⟩ = Z[1/√2, 1/√2]ᵀ = [1/√2, −1/√2]ᵀ = |−⟩
α = 1/√2 ≈ 0.707107, β = −1/√2 ≈ −0.707107
P(|0⟩) = P(|1⟩) = 0.5 (概率不变,因为 |−β|² = |β|²)
Bloch 球: X 轴负方向,θ=π/2, φ=πStep 4 — 第二个 H 门(回到计算基):
|ψ₄⟩ = H|ψ₃⟩ = H[1/√2, −1/√2]ᵀ = [0, 1]ᵀ = |1⟩
α = 0, β = 1
P(|0⟩) = 0%, P(|1⟩) = 100%
Bloch 球: |1⟩ = 南极,θ=π6.2 演化总结表
| 步骤 | 操作 | 状态 | α | β | P(|0⟩) | P(|1⟩) | Bloch 位置 | | ---- | ---- | ---- | - | - | ------ | ------ | ---------- | | 0 | 初始 | |0⟩ | 1+0i | 0+0i | 100% | 0% | 北极 | | 1 | H | |+⟩ | 1/√2 | 1/√2 | 50% | 50% | X+ 轴 | | 2 | X | |+⟩ | 1/√2 | 1/√2 | 50% | 50% | X+ 轴(不变) | | 3 | Z | |−⟩ | 1/√2 | −1/√2 | 50% | 50% | X− 轴 | | 4 | H | |1⟩ | 0+0i | 1+0i | 0% | 100% | 南极 |
关键洞察:H·Z·X·H|0⟩ = |1⟩,净效果等价于 X|0⟩。验证:
H·Z·X·H = H·Z·(X·H)
= H·Z·(H·Z) ← X·H = H·Z (验证: HXH=Z ⇒ X=HZH ⇒ XH=HZ)
= (H·Z·H)·Z
= X·Z ← HZH = X
...
实际上: H·Z·X·H = X ← 在忽略全局相位的意义下NOTE
X|+⟩ = |+⟩ 是因为 |+⟩ 是 X 门的本征态——。这是量子力学中最优美的现象之一:特定态在操作下保持不变,操作只改变态的"大小"(乘以本征值),不改变"方向"。类似经典力学中的谐振子不动点。
参考解答
练习1&2: complex_mult 和 complex_add
#include <math.h>
#include <stdio.h>
typedef struct {
double real;
double imag;
} Complex;
/* 复数乘法: (a+bi)(c+di) = (ac−bd) + (ad+bc)i */
static Complex complex_mult(Complex a, Complex b) {
Complex result;
result.real = a.real * b.real - a.imag * b.imag;
result.imag = a.real * b.imag + a.imag * b.real;
return result;
}
/* 复数加法: (a+bi)+(c+di) = (a+c)+(b+d)i */
static Complex complex_add(Complex a, Complex b) {
Complex result;
result.real = a.real + b.real;
result.imag = a.imag + b.imag;
return result;
}
/* 简单测试 */
int main(void) {
Complex a = {1.0, 2.0}; /* 1+2i */
Complex b = {3.0, 4.0}; /* 3+4i */
Complex prod = complex_mult(a, b);
printf("(%g+%gi)*(%g+%gi) = %g+%gi\n",
a.real, a.imag, b.real, b.imag,
prod.real, prod.imag);
/* 预期: (1+2i)*(3+4i) = -5+10i */
Complex sum = complex_add(a, b);
printf("(%g+%gi)+(%g+%gi) = %g+%gi\n",
a.real, a.imag, b.real, b.imag,
sum.real, sum.imag);
/* 预期: (1+2i)+(3+4i) = 4+6i */
return 0;
}要点:复乘的虚部是 ad+bc(加号!),不要写成减号。复加直接分量相加,无特殊注意点。
练习3: apply_gate — 矩阵-向量乘法
#include <math.h>
#include <stdio.h>
typedef struct { double real; double imag; } Complex;
static Complex complex_mult(Complex a, Complex b) {
Complex r;
r.real = a.real * b.real - a.imag * b.imag;
r.imag = a.real * b.imag + a.imag * b.real;
return r;
}
static Complex complex_add(Complex a, Complex b) {
Complex r;
r.real = a.real + b.real;
r.imag = a.imag + b.imag;
return r;
}
/* 门矩阵常量 */
#define SQRT2_2 0.7071067811865476
static const Complex H[2][2] = {
{{SQRT2_2, 0}, {SQRT2_2, 0}},
{{SQRT2_2, 0}, {-SQRT2_2, 0}},
};
static const Complex X[2][2] = {
{{0, 0}, {1, 0}},
{{1, 0}, {0, 0}},
};
/* apply_gate: G · state → state (原地修改) */
static void apply_gate(const Complex gate[2][2], Complex state[2]) {
Complex new0 = complex_add(
complex_mult(gate[0][0], state[0]),
complex_mult(gate[0][1], state[1])
);
Complex new1 = complex_add(
complex_mult(gate[1][0], state[0]),
complex_mult(gate[1][1], state[1])
);
state[0] = new0;
state[1] = new1;
}
int main(void) {
Complex state[2] = {{1.0, 0.0}, {0.0, 0.0}}; /* |0⟩ */
/* H|0⟩ = (|0⟩+|1⟩)/√2 */
apply_gate(H, state);
printf("After H: α=(%g,%g), β=(%g,%g)\n",
state[0].real, state[0].imag,
state[1].real, state[1].imag);
/* 预期: α≈(0.707,0), β≈(0.707,0) */
/* X|+⟩ = |+⟩ */
apply_gate(X, state);
printf("After X: α=(%g,%g), β=(%g,%g)\n",
state[0].real, state[0].imag,
state[1].real, state[1].imag);
/* 预期: α≈(0.707,0), β≈(0.707,0) — 不变 */
return 0;
}核心逻辑:用临时变量 new0、new1 保存两个分量的计算结果,全部算完后再写回。每个乘法和加法都调用 complex_mult 和 complex_add。直接修改 state[0] 会导致 state[1] 的计算读到错误的值。
练习4&5: print_state 和 measure
#include <math.h>
#include <stdio.h>
typedef struct { double real; double imag; } Complex;
/* print_state — Dirac 符号输出,格式必须精确 */
static void print_state(const char *label, const Complex state[2]) {
printf("%s\n", label);
printf("( %+f %+fi )|0⟩ + ( %+f %+fi )|1⟩\n",
state[0].real, state[0].imag,
state[1].real, state[1].imag);
printf("\n");
}
/* measure — LCG 伪随机 + Born 规则 */
static int measure(const Complex state[2], unsigned int *seed) {
/* Born 规则: P(|0⟩) = |α|² */
double prob0 = state[0].real * state[0].real
+ state[0].imag * state[0].imag;
/* LCG 推进: glibc rand() 参数 */
*seed = (1103515245u * (*seed) + 12345u) & 0x7FFFFFFFu;
/* 归一化到 [0, 1) */
double r = (double)(*seed) / (double)0x80000000u;
if (r < prob0) return 0;
else return 1;
}
int main(void) {
/* |+⟩ = (|0⟩+|1⟩)/√2: prob0 = 0.5 */
Complex state[2] = {{0.707107, 0.0}, {0.707107, 0.0}};
unsigned int seed = 42u;
int counts[2] = {0, 0};
/* 1000 次测量 */
for (int i = 0; i < 1000; i++) {
int outcome = measure(state, &seed);
counts[outcome]++;
}
printf("|0⟩: %d (%.1f%%)\n",
counts[0], counts[0] * 100.0 / 1000);
printf("|1⟩: %d (%.1f%%)\n",
counts[1], counts[1] * 100.0 / 1000);
/* 预期: 各约 50%,如 |0⟩: ~492, |1⟩: ~508 */
return 0;
}print_state 的关键:%+f 格式说明符自动显示正负号(+0.707107 或 -0.707107)。measure 的关键:0x80000000u 必须是 unsigned int(带 u 后缀),否则有符号溢出是 UB。
练习6: main — 完整电路模拟
#include <math.h>
#include <stdio.h>
typedef struct { double real; double imag; } Complex;
static Complex complex_mult(Complex a, Complex b) {
Complex r;
r.real = a.real * b.real - a.imag * b.imag;
r.imag = a.real * b.imag + a.imag * b.real;
return r;
}
static Complex complex_add(Complex a, Complex b) {
Complex r;
r.real = a.real + b.real;
r.imag = a.imag + b.imag;
return r;
}
#define SQRT2_2 0.7071067811865476
static const Complex H[2][2] = {
{{SQRT2_2, 0}, {SQRT2_2, 0}},
{{SQRT2_2, 0}, {-SQRT2_2, 0}},
};
static const Complex X[2][2] = {
{{0, 0}, {1, 0}},
{{1, 0}, {0, 0}},
};
static const Complex Z[2][2] = {
{{1, 0}, {0, 0}},
{{0, 0}, {-1, 0}},
};
static void apply_gate(const Complex gate[2][2], Complex state[2]) {
Complex new0 = complex_add(
complex_mult(gate[0][0], state[0]),
complex_mult(gate[0][1], state[1])
);
Complex new1 = complex_add(
complex_mult(gate[1][0], state[0]),
complex_mult(gate[1][1], state[1])
);
state[0] = new0;
state[1] = new1;
}
static void print_state(const char *label, const Complex state[2]) {
printf("%s\n", label);
printf("( %+f %+fi )|0⟩ + ( %+f %+fi )|1⟩\n",
state[0].real, state[0].imag,
state[1].real, state[1].imag);
printf("\n");
}
static int measure(const Complex state[2], unsigned int *seed) {
double prob0 = state[0].real * state[0].real
+ state[0].imag * state[0].imag;
*seed = (1103515245u * (*seed) + 12345u) & 0x7FFFFFFFu;
double r = (double)(*seed) / (double)0x80000000u;
if (r < prob0) return 0;
else return 1;
}
int main(void) {
/* 1. 初始化为 |0⟩ = [1+0i, 0+0i] */
Complex state[2] = {{1.0, 0.0}, {0.0, 0.0}};
print_state("Initial state |0⟩:", state);
/* 2. H 门: |0⟩ → |+⟩ = (|0⟩+|1⟩)/√2 */
apply_gate(H, state);
print_state("After H gate (superposition):", state);
/* 3. X 门: |+⟩ → |+⟩ (本征态,不变) */
apply_gate(X, state);
print_state("After X gate (Pauli-X/NOT):", state);
/* 4. Z 门: |+⟩ → |−⟩ = (|0⟩−|1⟩)/√2 */
apply_gate(Z, state);
print_state("After Z gate (Pauli-Z):", state);
/* 5. H 门: |−⟩ → |1⟩ = [0, 1] */
apply_gate(H, state);
print_state("After second H gate:", state);
/* 6. 100 次测量 */
int counts[2] = {0, 0};
unsigned int seed = 42u;
for (int i = 0; i < 100; i++) {
int outcome = measure(state, &seed);
counts[outcome]++;
}
/* 7. 输出统计 */
printf("Measurement results (%d trials):\n", 100);
printf("|0⟩: %d (%.2f%%)\n",
counts[0], counts[0] * 100.0 / 100);
printf("|1⟩: %d (%.2f%%)\n",
counts[1], counts[1] * 100.0 / 100);
return 0;
}完整程序输出:
Initial state |0⟩:
( +1.000000 +0.000000i )|0⟩ + ( +0.000000 +0.000000i )|1⟩
After H gate (superposition):
( +0.707107 +0.000000i )|0⟩ + ( +0.707107 +0.000000i )|1⟩
After X gate (Pauli-X/NOT):
( +0.707107 +0.000000i )|0⟩ + ( +0.707107 +0.000000i )|1⟩
After Z gate (Pauli-Z):
( +0.707107 +0.000000i )|0⟩ + ( -0.707107 +0.000000i )|1⟩
After second H gate:
( +0.000000 +0.000000i )|0⟩ + ( +1.000000 +0.000000i )|1⟩
Measurement results (100 trials):
|0⟩: 0 (0.00%)
|1⟩: 100 (100.00%)核心逻辑解析:
- H 门产生叠加:
|0⟩ → |+⟩,α 和 β 都变为 1/√2 ≈ 0.707107 - X 门对 |+⟩ 无影响:因为 |+⟩ 的 α=β,交换后不变——|+⟩ 是 X 的本征态
- Z 门翻转相位:β 符号从正变负,但测量概率不变(
|−β|² = |β|²) - H 门回到基态:
H|−⟩ = |1⟩(利用 H²=I,H|−⟩ = H·H|1⟩ = |1⟩) - 测量确定性:最终状态是纯 |1⟩,prob0=0,100 次全部返回 |1⟩
对照检查:
complex_mult的 imag 是a.real*b.imag + a.imag*b.real(加号)吗?apply_gate用了临时变量吗?measure中0x80000000u带了u后缀吗?seed的初始值是42u吗?print_state的格式字符串里有%+f吗?
课堂讨论
apply_gate中如果不使用临时变量,先计算并写回state[0]再计算state[1],会发生什么?用 H 门作用在|0⟩上跟踪状态变化,解释错误产生的具体过程。- 如果改变门的作用顺序为 H→Z→X→H(与本题的 H→X→Z→H 不同),最终状态是什么?这说明了量子门的什么性质?
- 本题最终测量 100 次全部返回 |1⟩,这不是随机性"恰好"导致的——为什么?如果测量 |+⟩ 态(Step 1 后的状态),统计结果会怎样?
- 为什么本题使用硬编码 LCG 而不是
rand()?seed = 42u改变会发生什么? - 验证恒等式 HXH = Z 和 HZH = X。它们揭示了 Hadamard 门的什么深层性质?
- 一个同学写
complex_mult时虚部用了减号(a.real*b.imag - a.imag*b.real),但程序编译无错,测试 diff 失败。如何快速定位这个错误?
讨论答案
Q1: apply_gate 不用临时变量的错误追踪
以 H 门作用在 |0⟩ = [1, 0]ᵀ 为例:
正确 (使用临时变量):
new0 = 0.707·1 + 0.707·0 = 0.707 (读 state[0]=1)
new1 = 0.707·1 + (−0.707)·0 = 0.707 (读 state[0]=1)
写回: state[0]=0.707, state[1]=0.707 ✓ |+⟩
错误 (先写回 state[0]):
state[0] = 0.707·1 + 0.707·0 = 0.707 (读 state[0]=1, 写回 0.707)
state[1] = 0.707·0.707 + (−0.707)·0 = 0.5
^^^^^ 读到的 state[0] 已是 0.707 而非 1!
结果: state[0]=0.707, state[1]=0.5 ✗ 错误状态!这是"写后读"数据竞争——单线程程序中也存在,因为两个表达式存在依赖关系。正确做法:计算 new0 和 new1 时都读取原始的 state[0] 和 state[1],计算完再一起写回。
Q2: 门顺序不同的结果——量子门不对易
H→X→Z→H|0⟩ = |1⟩ (本题顺序)
H→Z→X→H|0⟩ = |0⟩ (交换 X 和 Z 的顺序)
验证:
已知 HXH = Z, HZH = X ← H 将 X 和 Z 共轭互换
HZXH|0⟩ = H·Z·X·H|0⟩
因 X|0⟩ = |1⟩, H|1⟩ = |−⟩, Z|−⟩ = |+⟩, H|+⟩ = |0⟩
→ 回到 |0⟩
这是因为 X 和 Z 不对易 (anti-commute):
XZ = −ZX
量子门大多数不对易——顺序至关重要!
这不同于经典逻辑门 (AND、OR 可交换)。Q3: 测量确定性与概率性
本题最终状态 |ψ₄⟩ = |1⟩ = [0, 1]ᵀ:
- prob0 = 0²+0² = 0
- r ∈ [0, 1) 永远 ≥ 0 → r < 0 永远不成立
- → 100 次全部返回 |1⟩,确定性的,不是"恰好"
如果测量 |+⟩ = [1/√2, 1/√2]ᵀ(Step 1 后):
- prob0 = 0.5
- LCG 产生 [0,1) 上均匀分布的伪随机列
- 预期 counts[0] ≈ 50, counts[1] ≈ 50
- 具体数值取决于 seed 和序列——seed=42u 时特定结果可精确预测
关键理解:量子测量的概率性是本质性的(Born 规则),不是信息不完备。但纯态(α 或 β 为 0)时概率退化到确定性。
Q4: LCG vs rand() 的设计选择
rand() 在不同平台/实现下行为不同:
- glibc (Linux): 内部 LCG,参数 (1103515245, 12345)
- musl (Alpine): 不同算法
- MSVC: 又一种算法
硬编码 LCG 的好处:
- 所有平台产生相同序列 → expected_output.txt 可精确预测
- 学员能直接看到随机数的生成公式,理解"伪随机"的本质
seed = 42u改变 → 随机序列不同 → 统计结果不同 → diff 测试失败
/* 如果 seed 初始值不是 42u,如 seed = 123u:
* LCG 从 123 开始产生完全不同序列
* → 即使最终状态相同 (纯 |1⟩),序列值不同
* → 输出可能也不同 (如输出格式包含 seed 值则 diff 失败)
*
* 如果 seed = 0:
* r 序列: 某特定序列
* 但 seed=0 时 LCG 第一轮结果为 12345/2³¹
* 不影响 |1⟩ 态测量 (r 总 ≥ 0)
*/Q5: H 门作为基底转换器——Pauli 矩阵的对偶性
恒等式 HXH = Z:
HXH = 1/√2 [[1, 1]] [[0, 1]] 1/√2 [[1, 1]]
[[1,-1]] [[1, 0]] [[1,-1]]
= 1/2 [[1, 1]] [[1,-1]] = 1/2 [[1·1+1·1, 1·(-1)+1·(-1)]]
[[1,-1]] [[1, 1]] [[1·1+(-1)·1, 1·(-1)+(-1)·1]]
= 1/2 [[2, -2]] = [[1, 0]] = Z ✓
[[0, 0]] [[0, -1]]恒等式 HZH = X(同理)。
深刻含义:Hadamard 门是 X 基底和 Z 基底之间的转换器:
- 在 Z 基底(计算基
|0⟩,|1⟩)中:X 是比特翻转,Z 是相位翻转 - 在 X 基底(
|+⟩,|−⟩)中:角色互换——Z 变成比特翻转,X 变成相位翻转
这是量子力学中"基底变换"的完美示例——同一个物理操作在不同基底中看起来完全不同。H 门的作用相当于将坐标系旋转到 X-Z 平面的对角线。
Q6: complex_mult 符号错误定位方法
症状: 程序编译通过,但输出与 expected_output 不一致。
诊断步骤:
1. 独立测试 complex_mult:
已知 (1+2i)*(3+4i) = (−5)+10i
打印 complex_mult({1,2}, {3,4}) 的结果
若得 (−5)+2i → 虚部符号错误 (用了减号)
2. 观察错误传播:
如果 complex_mult 符号错,H 门结果也会错:
H|0⟩ = [0.707, 0.707] 正确
但计算的 g₀₀*state[0] 就会偏掉
→ 连锁反应导致最终状态不是 |1⟩
3. 二分法定位:
先检查 complex_mult 是否正确(独立测试)
再检查 complex_add(简单,大概率对)
再检查 apply_gate(用已知输入输出验证)
最后检查 main 流程
4. 精确的复乘验证集:
(0.707+0i)*(1+0i) = 0.707+0i
(1+0i)*(0+1i) = 0+1i (i*i = -1: 0+(-1) = -1+0i 只要不用减号)课后练习
实现 Pauli-Y 门。Y = [[0,−i],[i,0]],是本题未使用的第三个 Pauli 矩阵。验证
Y|0⟩ = i|1⟩和Y|1⟩ = −i|0⟩。注意:Y 门首次引入了需要虚部非零的Complex常量。知识点提示:Y 门的矩阵元素包含虚数 i,需要用
(Complex){0, ±1}表示。Y|0⟩的结果 β = i(全虚数),验证|β|² = 1仍满足归一化。参考解答
c#include <math.h> #include <stdio.h> typedef struct { double real; double imag; } Complex; static Complex complex_mult(Complex a, Complex b) { Complex r; r.real = a.real * b.real - a.imag * b.imag; r.imag = a.real * b.imag + a.imag * b.real; return r; } static Complex complex_add(Complex a, Complex b) { Complex r; r.real = a.real + b.real; r.imag = a.imag + b.imag; return r; } static void apply_gate(const Complex gate[2][2], Complex state[2]) { Complex new0 = complex_add( complex_mult(gate[0][0], state[0]), complex_mult(gate[0][1], state[1]) ); Complex new1 = complex_add( complex_mult(gate[1][0], state[0]), complex_mult(gate[1][1], state[1]) ); state[0] = new0; state[1] = new1; } /* Pauli-Y: [[0, -i], [i, 0]] */ static const Complex Y[2][2] = { {{0, 0}, {0, -1}}, /* row 0: [0, -i] */ {{0, 1}, {0, 0}}, /* row 1: [i, 0] */ }; int main(void) { Complex state[2]; /* Y|0⟩ = i|1⟩ = [0, i] */ state[0] = (Complex){1.0, 0.0}; state[1] = (Complex){0.0, 0.0}; apply_gate(Y, state); printf("Y|0⟩ = (%.1f%+.1fi)|0⟩ + (%.1f%+.1fi)|1⟩\n", state[0].real, state[0].imag, state[1].real, state[1].imag); /* 预期: (0.0+0.0i)|0⟩ + (0.0+1.0i)|1⟩ = i|1⟩ */ /* Y|1⟩ = -i|0⟩ = [-i, 0] */ state[0] = (Complex){0.0, 0.0}; state[1] = (Complex){1.0, 0.0}; apply_gate(Y, state); printf("Y|1⟩ = (%.1f%+.1fi)|0⟩ + (%.1f%+.1fi)|1⟩\n", state[0].real, state[0].imag, state[1].real, state[1].imag); /* 预期: (0.0-1.0i)|0⟩ + (0.0+0.0i)|1⟩ = -i|0⟩ */ /* Y² = I 验证 */ state[0] = (Complex){1.0, 0.0}; state[1] = (Complex){0.0, 0.0}; apply_gate(Y, state); /* Y|0⟩ = i|1⟩ */ apply_gate(Y, state); /* Y(i|1⟩) = i·(−i|0⟩) = −i²|0⟩ = |0⟩ */ printf("Y²|0⟩ = (%.1f%+.1fi)|0⟩ + (%.1f%+.1fi)|1⟩\n", state[0].real, state[0].imag, state[1].real, state[1].imag); /* 预期: 回到 |0⟩ */ return 0; }关键:Y 门的 (0,1) 位置是 −i →
{0, −1};(1,0) 位置是 i →{0, 1}。验证 Y²=I——Y 门施加两次回到原态,乘以全局相位 −1 后归一到同一物理态。验证酉矩阵保持归一化。编写
double norm(const Complex state[2])函数计算|α|²+|β|²。在每次apply_gate后调用,验证其始终为 1.0。讨论浮点误差可能的范围。知识点提示:每次门操作后,
|α|²+|β|²应保持为 1.0。但浮点运算(特别是 1/√2 的乘法)会引入微小误差,通常 < 10^{-15}。参考解答
c#include <math.h> #include <stdio.h> typedef struct { double real; double imag; } Complex; /* ... 此处省略 complex_mult, complex_add, apply_gate, H/X/Z 定义 ... */ /* 计算量子态范数 |α|² + |β|² */ static double norm(const Complex state[2]) { double mod0 = state[0].real * state[0].real + state[0].imag * state[0].imag; double mod1 = state[1].real * state[1].real + state[1].imag * state[1].imag; return mod0 + mod1; } int main(void) { Complex state[2] = {{1.0, 0.0}, {0.0, 0.0}}; printf("Initial norm: %.16f\n", norm(state)); /* 1.0 */ /* 施加各门并验证范数 */ apply_gate(H, state); printf("After H: norm = %.16f\n", norm(state)); apply_gate(X, state); printf("After X: norm = %.16f\n", norm(state)); apply_gate(Z, state); printf("After Z: norm = %.16f\n", norm(state)); apply_gate(H, state); printf("After H: norm = %.16f\n", norm(state)); /* 验证归一化是否保持 */ double final_norm = norm(state); if (fabs(final_norm - 1.0) < 1e-14) printf("Normalization preserved ✓ " "(error: %e)\n", fabs(final_norm - 1.0)); else printf("Normalization violated! " "(error: %e)\n", fabs(final_norm - 1.0)); return 0; } /* 预期输出: * Initial norm: 1.0000000000000000 * After H: norm = 0.9999999999999999 (或 1.0000000000000002) * After X: norm = 1.0000000000000000 * After Z: norm = 1.0000000000000000 * After H: norm = 1.0000000000000000 * Normalization preserved ✓ (error: ~1e-16) * * 注: H 门使用 1/√2 ≈ 0.7071067811865476 (约 16 位精度), * 平方后舍入误差在 ~1e-16 量级,完全可接受。 */浮点误差分析:
(0.7071067811865476)² ≈ 0.5精确到 ~1e-16,四次操作累积误差也 < 1e-14,远小于double精度极限。实现通用旋转门。编写
void rx(double theta, Complex state[2])实现绕 X 轴的旋转门Rx(θ) = [[cos(θ/2), −i·sin(θ/2)],[−i·sin(θ/2), cos(θ/2)]]。验证Rx(π) = −iX(等价于 X 门乘以全局相位 −i)。知识点提示:需要
<math.h>的cos()和sin()。全局相位 −i 不影响物理可观测结果——这是量子力学中"物理态是射线"的体现。参考解答
c#include <math.h> #include <stdio.h> typedef struct { double real; double imag; } Complex; /* ... 省略 complex_mult, complex_add, apply_gate, print_state ... */ /* Rx(θ) — 绕 X 轴旋转 θ 弧度 * Rx(θ) = [[ cos(θ/2), -i·sin(θ/2) ], * [ -i·sin(θ/2), cos(θ/2) ]] */ static void rx(double theta, Complex state[2]) { double c = cos(theta / 2.0); double s = sin(theta / 2.0); Complex gate[2][2] = { {{c, 0}, {0, -s}}, /* row 0: [cos(θ/2), -i·sin(θ/2)] */ {{0, -s}, {c, 0}}, /* row 1: [-i·sin(θ/2), cos(θ/2)] */ }; apply_gate(gate, state); } int main(void) { Complex state[2]; /* Rx(π) 应等价于 X 门 */ printf("Rx(π) applied to |0⟩:\n"); state[0] = (Complex){1.0, 0.0}; state[1] = (Complex){0.0, 0.0}; rx(M_PI, state); printf(" α = %.3f%+.3fi\n", state[0].real, state[0].imag); printf(" β = %.3f%+.3fi\n", state[1].real, state[1].imag); /* 预期: α = 0, β = 0-1i = -i * X|0⟩ = [0, 1], 这里的 [0, -i] 差了一个全局相位 −i * 物理上等价——测量概率分别为 0 和 1 */ /* Rx(π/2) 产生非平凡叠加态 */ printf("\nRx(π/2) applied to |0⟩:\n"); state[0] = (Complex){1.0, 0.0}; state[1] = (Complex){0.0, 0.0}; rx(M_PI / 2.0, state); /* 预期: α = 1/√2, β = -i/√2 (|+i⟩ 的转置共轭) */ printf(" α = %.6f%+.6fi\n", state[0].real, state[0].imag); printf(" β = %.6f%+.6fi\n", state[1].real, state[1].imag); /* prob0 = 0.5, prob1 = 0.5 */ return 0; }全局相位:
Rx(π)给出的结果是[0, −i]ᵀ,而 X 门给出[0, 1]ᵀ。两者差了一个全局相位因子 −i。但测量概率|β|² = |−i|² = 1相同——全局相位不影响任何物理可观测结果。这是量子力学中"物理态是 Hilbert 空间中的射线(ray)"的体现。
参考资料
- Nielsen & Chuang《Quantum Computation and Quantum Information》— 量子计算标准教材,第 1-2 章覆盖单量子比特和量子门
- Michael A. Nielsen《Quantum Computing for the Very Curious》— 互动式量子计算入门
- IBM Quantum Experience (
quantum-computing.ibm.com) — 在线量子计算平台,可用 Qiskit 编写量量子电路 - C99 标准 §7.3 —
<complex.h>复数算术库(本题手动实现的底层原理) - glibc
rand()源码 — LCG 参数 (1103515245, 12345, 2³¹) 的来源 - Quantum Inspire — qubit visualization — Bloch 球交互式可视化工具
"I think I can safely say that nobody understands quantum mechanics." — Richard Feynman