1. BB84 单光子源与偏振编码的密钥生成流程实现

任务拆解:一次 BB84 密钥协商要跑通哪些环节

本例的目标不是讲清 BB84 的全部物理背景,而是用一段可运行的 Python 代码,把“单光子偏振态制备 → 量子信道传输 → 单光子测量 → 基矢比对 → 筛选后密钥一致性校验”这条链路完整走一遍,并输出误码率。为了让结果可复现,我们固定随机种子,用理想信道(无窃听、无损耗)作为基线,此时筛选后误码率应为 0;随后再人为翻转少量比特,观察误码率如何变化,从而验证“误码率统计”这一环节确实在工作。

BB84 的核心约定如下:Alice 随机选择两种基矢之一——直角基(Z 基,用比特 0/1 表示)或对角基(X 基,用比特 +/− 表示);在每个基矢下随机选择比特值,制备对应的单光子偏振态。Bob 独立随机选择测量基矢进行测量。只有当双方基矢一致时,测量结果才具有确定性,这些比特构成筛选后密钥(sifted key)。

Alice 制备 随机基矢 b_A ∈ {Z, X} 随机比特 a ∈ {0, 1} 偏振态: Z: 0→|H⟩, 1→|V⟩ X: 0→|+⟩, 1→|−⟩ 量子信道 单光子传输 (本例:理想无损耗) Bob 测量 随机基矢 b_B ∈ {Z, X} 测量得到比特 b 基矢一致 → 结果确定 基矢不同 → 结果随机 经典信道 公开比对基矢 保留 b_A = b_B 的比特 筛选后密钥 Alice 与 Bob 各持一份 统计误码率 QBER 一致性判断 QBER = 0 → 密钥一致 实线:量子/经典数据流 虚线:基矢信息经经典信道公开

关键实现:偏振态制备与测量的编码方式

代码中用一个整数同时编码“基矢”和“比特值”,避免物理态与逻辑比特混淆。约定:0 表示 Z 基比特 0(|H⟩),1 表示 Z 基比特 1(|V⟩),2 表示 X 基比特 0(|+⟩),3 表示 X 基比特 1(|−⟩)。基矢由 state // 2 得到(0 为 Z,1 为 X),比特值由 state % 2 得到。Bob 的测量规则是:若测量基与制备基一致,则结果等于原比特;若不一致,则结果在 0/1 上等概率随机——这正是 BB84 中“非正交基测量引入不确定性”的最小化模拟。

import random

def prepare_state(rng):
    """Alice 制备:返回 0..3,编码 (基矢, 比特)。"""
    basis = rng.randint(0, 1)      # 0=Z, 1=X
    bit = rng.randint(0, 1)
    return basis * 2 + bit

def measure(state, bob_basis, rng):
    """Bob 测量:返回测量比特。"""
    alice_basis = state // 2
    alice_bit = state % 2
    if bob_basis == alice_basis:
        return alice_bit            # 基矢一致,结果确定
    return rng.randint(0, 1)        # 基矢不同,结果随机

def run_bb84(n_bits=1000, seed=2024, flip_positions=None):
    rng = random.Random(seed)
    alice_states = [prepare_state(rng) for _ in range(n_bits)]
    bob_bases = [rng.randint(0, 1) for _ in range(n_bits)]
    bob_results = [measure(s, b, rng) for s, b in zip(alice_states, bob_bases)]

    # 基矢比对与筛选
    sifted_alice, sifted_bob = [], []
    for s, bb, br in zip(alice_states, bob_bases, bob_results):
        if s // 2 == bb:
            sifted_alice.append(s % 2)
            sifted_bob.append(br)

    # 人为注入错误(用于验证误码率统计)
    if flip_positions:
        for p in flip_positions:
            if p < len(sifted_bob):
                sifted_bob[p] ^= 1

    errors = sum(a != b for a, b in zip(sifted_alice, sifted_bob))
    qber = errors / len(sifted_alice) if sifted_alice else 0.0
    return len(sifted_alice), errors, qber

if __name__ == "__main__":
    n, err, qber = run_bb84()
    print(f"筛选后密钥长度: {n}, 错误比特数: {err}, QBER: {qber:.4f}")

    n2, err2, qber2 = run_bb84(flip_positions=[0, 5, 10, 20, 50])
    print(f"注入5处翻转后: 长度 {n2}, 错误 {err2}, QBER: {qber2:.4f}")

运行结果与判读

在理想信道、固定种子 2024、n_bits=1000 的设定下,筛选后密钥长度约为 500(因为双方基矢一致的概率为 1/2),错误比特数为 0,QBER 为 0.0000。第二次运行人为翻转了筛选后密钥中 5 个位置的比特,错误数变为 5,QBER 约为 0.01。这两组结果说明:基矢比对逻辑正确(只保留一致基矢的比特),且误码率统计能如实反映密钥差异。

判读要点:本例是“理想基线”,QBER=0 是预期结果而非“协议一定安全”的证明。真实系统中光源多光子、信道损耗、探测器暗计数都会抬高 QBER;后续案例会引入窃听模型与安全阈值判定,届时 QBER 才成为安全性判据。

可复现的验证清单

检查项预期现象说明
筛选后长度约等于 n_bits/2基矢一致概率 1/2,允许统计波动
理想信道 QBER0.0000无窃听、无噪声时筛选比特完全一致
注入 5 处翻转错误数 = 5,QBER ≈ 5/筛选长度验证误码统计链路有效
更换随机种子长度与 QBER 小幅波动理想信道下 QBER 仍应为 0

把 flip_positions 改为空列表并更换种子,可反复确认“理想信道下筛选后密钥始终一致”;把翻转位置数量逐步增大,可观察 QBER 线性上升,为下一例的窃听检测阈值判定建立直观基线。