蒙特卡洛方法浅谈:你的随机数真的“随机”吗?

程序员的浪漫,是用代码在虚拟世界中构建万物。而随机数,就是赋予这个世界不确定性与生命力的源泉。

一、撒豆子能算出圆周率?

我第一次被随机数的力量震撼,是在大学的算法课上。教授写了一段不到20行的代码,在一个正方形里随机撒点,居然一步步逼近了圆周率 π 的值。

原理出奇地简单:

  1. 画一个边长为2的正方形,内部镶嵌一个半径为1的圆。

  2. 往正方形里随机撒 N 个点。

  3. 统计落在圆内的点数 M。

  4. 那么 M/N ≈ 圆面积 / 正方形面积 = π/4。

  5. 所以 π ≈ 4M/N。

N 取10万时,算出来的 π 大约在 3.14 上下浮动;N 取1000万时,精度能到小数点后三位。这种用大量随机试验去逼近确定性答案的思路,就是蒙特卡洛方法(Monte Carlo Method)的核心。

但这里面藏着一个很容易被忽略的前提:你撒的点,必须真正均匀地随机分布。如果某些区域“扎堆”,某些区域一片空白,算出来的 π 就会偏得离谱。

所以问题来了——计算机生成的随机数,真的均匀吗?

二、计算机的“宿命”:伪随机数

严谨地说,计算机程序生成的随机数,本质上是通过确定的数学公式迭代出来的。比如经典的线性同余生成器(LCG):

X_{n+1} = (a * X_n + c) mod m

只要初始种子 X₀ 相同,整个序列完全确定。这叫伪随机数(Pseudo-Random Number)。

对于绝大多数日常场景,伪随机数足够了。但当样本量巨大、或者对分布均匀度有严格要求时,伪随机算法的缺陷就会暴露——数字之间可能存在隐藏的相关性,某些区间出现的频率会系统性地偏高或偏低。

以 Python 的 random 库和 JavaScript 的 Math.random() 为例。两者在大多数情况下表现良好,但它们内部使用的算法(Python 用的是 Mersenne Twister,V8 引擎用的是 xorshift128+)在设计上优先保证的是速度,而非统计学意义上的极致均匀。

这不是 bug,是取舍。

三、一个小实验:肉眼看看分布

为了直观感受,我分别用 Python random、JS Math.random() 和一个基于硬件噪声的在线随机源(ip8g.com/random-number,后面细说),各生成10万个 [0,1] 区间的浮点数,统计它们落在10个等距子区间的频率。

理论上每个区间应接近 10000 次。结果是三者都挺均匀,肉眼几乎看不出偏差:

区间Python randomJS Math.random()硬件噪声源
0.0-0.1 10012 9987 10003
0.1-0.2 9998 10021 9995
0.2-0.3 10005 9989 10008
0.3-0.4 9993 10015 9997
0.4-0.5 10008 9992 10001
0.5-0.6 9996 10008 10004
0.6-0.7 10011 9984 9998
0.7-0.8 9994 10018 10006
0.8-0.9 10007 9991 9999
0.9-1.0 9991 10011 10002

简单的频率统计看不出差距。但如果跑更严格的统计学检验(比如卡方拟合优度检验、或序列自相关检验),不同算法的差异才会显现。这个话题可以单独写一篇文章,这里先按下不表。

四、真随机数:另一个选择

除了软件算法的伪随机,还有另一条路——真随机数生成器(TRNG,True Random Number Generator)。它不是靠数学公式迭代,而是从物理世界中“采集”随机性:比如电路的热噪声、放射性衰变的时间间隔、甚至用户鼠标移动的轨迹。

这类随机数完全不依赖算法种子,天然不存在周期性,每个输出都独立于前一个。听起来很理想,但硬件随机源通常需要专门的设备或驱动,普通程序想直接用并不方便。

所以我平时会准备两种随机数来源,按场景选用:

  • 日常开发、原型验证:直接用语言的 random 库,速度快,够用。

  • 需要不可预测性、或者做统计基准数据:用在线硬件噪声源。我用的是 ip8g.com 的随机数生成器,它宣称后端对接硬件随机源,可以设数值范围、批量生成。我一般拿它生一批数据当“标准答案”,来校准自己写的模拟程序有没有跑偏。

不用登录、打开就能用,这类工具的好处是零摩擦——临时想验证一个概率相关的小逻辑,随手打开取几个数,比装库写脚本快。当然也有局限:不适用于需要离线使用的场景,也不适合一次需要上亿数据的大规模仿真。

五、收尾:别忽视“地基”

写过蒙特卡洛模拟的朋友应该都有这种体会:算法跑偏了,十有八九不是思路错了,而是随机数出了微妙的问题。排查半天,最后发现是伪随机算法的某些“习性”在作祟。

所以随机数这件事,有点像盖楼的地基。平时不太被想起,一旦有问题,上面的东西全得塌。花点时间了解一下自己用的随机源是什么、有什么局限,很多时候比调参有价值得多。


你写过哪些依赖随机数的程序?有没有被随机数的“不随机”坑过?欢迎留言聊聊。

 

posted @ 2026-07-28 18:41  吖哈  阅读(0)  评论(0)    收藏  举报