我在前端写了个量子骰子,结果它一停下来就忘了自己是谁

有个事儿我一直觉得特别诡异——我写了一个模拟量子骰子的前端小工具,它在浏览器里转得风生水起,同时处于1到6所有点数的叠加态。但只要我一按“观测”按钮,它就“坍缩”成一个确定的结果,然后,它就再也回不去了。

就像那个骰子完全忘了自己刚才还是所有可能性的集合体。

这背后其实是量子力学里最反直觉的一个设定:测量会破坏叠加态,而且这个过程不可逆。 你在前端用几行代码就能把这事儿模拟得明明白白,甚至还能亲眼看着概率波形怎么在一瞬间塌成一个点。

那个骰子在观测前到底在干嘛

我第一次接触“量子叠加”这个概念时,脑子里想的是一枚硬币在空中翻滚——正面和反面交替出现,快得分不清。但量子叠加根本不是这么回事。

在观测之前,一个量子比特(比如我们的骰子)不是“快速切换”于各个状态之间,而是真的同时处于所有可能状态的线性组合里。用行话说,它的状态可以写成这样:

|ψ⟩ = α|1⟩ + β|2⟩ + γ|3⟩ + δ|4⟩ + ε|5⟩ + ζ|6⟩

每个系数(α、β这些)的模平方,代表你测量时得到对应结果的概率。比如α²=0.3,那你观测到点数为1的概率就是30%。

关键来了——这些系数不是经典概率那种“我们不知道所以猜一个”,而是系统本身就模糊地处于这些状态的叠加之中。这不是信息的缺失,是世界的底层规则。

我写那个量子骰子模拟时,前端代码里其实就是在维护这么一个系数数组。骰子在屏幕上转的时候,后台的量子态向量一直处于六个基态的叠加中,每个基态带一个复数振幅。看起来它在转,实际上它在“弥漫”。

// 量子骰子的叠加态表示
class QuantumDie {
  constructor() {
    // 初始化为均匀叠加态,每个面概率相等
    const amplitude = 1 / Math.sqrt(6);
    this.state = [
      { real: amplitude, imag: 0 }, // |1⟩
      { real: amplitude, imag: 0 }, // |2⟩
      { real: amplitude, imag: 0 }, // |3⟩
      { real: amplitude, imag: 0 }, // |4⟩
      { real: amplitude, imag: 0 }, // |5⟩
      { real: amplitude, imag: 0 }, // |6⟩
    ];
  }
}

这个state数组就是骰子的全部身份。只要没人观测,它可以一直这么“既是1又是2又是3又是4又是5又是6”地存在下去。你可以对它做各种幺正变换——相当于量子逻辑门操作——让概率分布变来变去,但它始终是个叠加态。

观测按钮按下去的那一刻发生了什么

我在页面上放了一个硕大的红色按钮,写着“观测”。每次点它,骰子就停了,显示一个确定的点数。但如果你以为这只是从0到1之间取个随机数然后映射到1-6,那就错过量子力学最精彩的部分了。

测量不是“看一眼”,而是让量子系统与宏观经典世界发生不可逆的相互作用。 这个相互作用会强制系统选择一个确定的本征态,而且选择哪个,严格按照振幅模平方的概率来。

我前端的实现逻辑是这样的:先根据当前量子态的振幅计算每个结果的概率分布,然后用Math.random()做一次随机抽样。但真正的量子测量比这深刻得多——测量本身改变了系统的状态。

measure() {
  // 计算概率分布:每个结果的概率 = |振幅|²
  const probabilities = this.state.map(amp => 
    amp.real ** 2 + amp.imag ** 2
  );
  
  // 用随机数模拟量子随机性
  const random = Math.random();
  let cumulative = 0;
  let outcome = 0;
  
  for (let i = 0; i < probabilities.length; i++) {
    cumulative += probabilities[i];
    if (random < cumulative) {
      outcome = i;
      break;
    }
  }
  
  // 坍缩:状态变为纯态,只有测量结果对应的基态概率为1
  this.state = this.state.map((_, index) => ({
    real: index === outcome ? 1 : 0,
    imag: 0
  }));
  
  return outcome + 1; // 返回1-6的点数
}

看到最后那几行了吗?测量后,整个量子态数组被我暴力重置了——只有测量结果对应的那个基态振幅变成1,其他的全部归零。这就是“坍缩”。

在真正的量子力学里,这个过程更正式的说法叫“投影测量”:你把态矢量投影到测量算符的某个本征子空间上。结果就是,叠加态没了,骰子“忘了”自己曾经是所有可能性的组合。

我每次点那个红色按钮,都能在开发者控制台里看到state数组从六个非零复数瞬间变成只有一个1和五个0。那个瞬间总让我想起《黑衣人》里记忆消除器的闪光——啪,全忘。

为什么马路上看不到叠加态的汽车

这个问题我刚开始学量子计算时也困惑过。如果叠加态是世界的底层规则,那为什么我们日常生活中从来见不到一辆“同时向左转和向右转”的车?

答案不是“宏观物体没有量子效应”,而是退相干——系统与环境纠缠后,叠加态的相位信息泄露到环境中,从外部看就好像坍缩了。

想象一下,你的量子骰子不是孤立的。空气分子撞到它、光子打到它、甚至它自身的分子热运动——每一个相互作用都在“测量”它的状态。这些测量不需要一个穿白大褂的科学家来做,环境本身就是永不停歇的观测者。

一个宏观物体(比如汽车)包含大约10²³个粒子,它与环境的纠缠在飞秒级别就完成了退相干。所以你在马路上看到的是确定的世界,不是因为你特殊,而是因为你的感知尺度太慢了——等你看到的时候,坍缩已经发生了万亿次。

这在前端模拟里有个特别直观的体现。我试着给量子骰子加了一个“环境噪声”参数,每过几毫秒就对量子态做一次微小的随机扰动。结果不用我按观测按钮,骰子自己就慢慢从一个均匀叠加态变成了近似经典概率分布的东西——虽然没有完全坍缩到某一个值,但各个基态之间的量子相干性(那些复数振幅的相对相位)全被洗掉了。

// 模拟退相干:每次迭代加入随机相位噪声
decohere(strength = 0.01) {
  this.state = this.state.map(amp => {
    const noise = (Math.random() - 0.5) * strength;
    const angle = Math.atan2(amp.imag, amp.real) + noise;
    const magnitude = Math.sqrt(amp.real ** 2 + amp.imag ** 2);
    return {
      real: magnitude * Math.cos(angle),
      imag: magnitude * Math.sin(angle)
    };
  });
}

这段代码跑个几百次迭代后,虽然概率分布还在,但量子干涉效应消失了。这时候你再做测量,结果和经典随机骰子没什么区别。量子计算的威力就在那些相位关系里,退相干等于把量子计算机降级成了经典计算机。

随机数能模拟量子随机性吗

这个问题我自己纠结过很久。Math.random()是伪随机数生成器(PRNG),底层用的是确定性算法(Chrome的V8引擎用的是xorshift128+),给它同一个种子,出来的序列完全可预测。而量子测量产生的随机性,按照主流解释(哥本哈根诠释),是本体论层面上的真随机——不是“我们不知道所以看起来随机”,而是宇宙本身就没有确定的结果,直到测量的那一刻。

所以严格来说,用Math.random()模拟量子测量,从一开始就“不量子”。但我后来想通了——对于前端模拟来说,这不是bug,而是对退相干边界的一个诚实建模。

你想,当量子骰子的测量结果通过API传到服务器、渲染到屏幕上、被你肉眼看到,这整个链条里每一步都是经典信息处理。你看到的结果,早就不是原始量子随机性的直接呈现了,而是经过层层经典化之后的产物。Math.random()产生的伪随机数,在统计分布上和真随机无法区分(只要你不去较真它的种子),对于理解“测量导致概率坍缩”这个核心概念来说,完全够用。

不过我后来还是手痒,接了一个真随机数API——澳大利亚国立大学的ANU Quantum Random Numbers服务器,它用真空涨落产生真随机比特。替换掉Math.random()后,骰子行为没有任何可观测的区别。这反而让我更确认了一件事:对于理解量子测量,重要的不是随机数的来源,而是坍缩这个动作本身——测量强制系统从叠加态跳到一个本征态,并且这个跳跃的概率由振幅模平方决定。

那个骰子真的“忘了”吗

回到标题的问题。我那个前端量子骰子,一停下来就忘了自己是谁——从代码层面看,就是state数组被重置了。但要说它“忘了”,其实不准确。

更精确的说法是:测量后的骰子处于一个新的量子态——一个纯的本征态。它的过去(叠加态)不能通过任何后续测量恢复,因为测量破坏了相干信息。

信息去哪了?在真实物理系统里,它泄露到了环境中。在我的前端模拟里,它直接被this.state.map(...)那行代码覆盖了。无论哪种情况,你都没法把骰子倒回观测前那种“既是1又是2又是3又是4又是5又是6”的状态。

这让我想起一个更哲学的问题:如果观测前的叠加态包含了所有可能性,那观测后的确定态是不是“更少”的东西?从信息熵的角度看,叠加态的冯·诺依曼熵是log(6)(最大混合态),观测后变成0(纯态)。信息变少了。但你获得了确定的测量结果——这个结果对你(宏观观察者)来说是新增的信息。量子测量就是这样一种奇怪的交易:你用“系统原本的量子 richness”换了一个“经典世界能理解的确定答案”。

所以下次你在浏览器里跑一个量子模拟,点下观测按钮,看到结果坍缩成单一数值时,可以想想——那个瞬间,你不仅仅是在调用一个函数,而是在复现宇宙最基本的一个操作:让可能性变成现实。


常见问题

为什么量子计算机要尽量避免测量,不是最后才测吗?

量子计算的威力在于叠加态和量子干涉。如果你在计算中间就测量,等于强制系统坍缩,之前积累的所有量子并行性瞬间消失,相当于把一台量子计算机降级成了扔硬币机器。所以量子算法通常只在最后一步测量,把计算结果提取出来。

Math.random()和量子随机数到底差在哪?

Math.random()是算法生成的伪随机数,给定种子完全可以复现。量子随机数来自物理过程(比如放射性衰变、真空涨落),按照量子力学标准解释,是真随机——连宇宙自己都不知道下一个bit是什么。但在统计测试里,好的PRNG和量子随机数看不出区别。

退相干和测量坍缩是一回事吗?

不是。退相干是系统与环境纠缠导致相干性丢失的过程,它解释了为什么宏观世界看不到叠加态,但它本身不挑选哪一个结果——它只是让概率分布变成经典的。测量坍缩(按哥本哈根诠释)是额外的一个假设,它说在测量那一刻,系统随机跳到一个本征态。这两个概念配合使用,才能完整描述“为什么观测会破坏叠加态”。

我能不能在前端模拟真正的量子计算?

不能跑真正的量子算法,因为浏览器里的JavaScript是经典图灵机,模拟量子比特的数量每增加一个,需要的经典计算资源翻倍。模拟20个量子比特就需要存储2²⁰(约一百万)个复数振幅,30个就到十亿级别。但模拟几个量子比特来理解叠加、纠缠、测量这些概念,前端完全够用,而且很直观。