随机数生成其实挺困难的
(伪)随机数生成其实还挺复杂的,因为你可能会搞出一个看似均匀而随机的分布,但是在高维度上会出问题的算法。考虑这样一个 LCG:
其中,随机数种子 是一个奇数。这个 LCG 的名字叫做 RANDU.
如果我们只是用来生成一维的数据的话,它看起来还是挺随机的。我们简单地生成一些值:
const size = 10000
const lcg_randu = (s) => (65539 * s) % 0x80000000
const results = new Array(size)
let seed = 1;
for (let i = 0; i < size; i++) {
seed = lcg_randu(seed)
results[i] = seed / 0x80000000
}
然后验证每个区间内的样值个数[1]:
const interval_width = 0.05
const interval_count = 1 / interval_width
const interval_lots = new Array(interval_count)
for (let i = 0; i < interval_count; i++) {
interval_lots[i] = {
from: i * interval_width,
to: (i + 1) * interval_width,
count: 0
}
}
for (let i = 0; i < size; i++) {
const sample = results[i]
for (let j = 0; j < interval_count; j++) {
const lot = interval_lots[j]
if (sample >= lot.from && sample < lot.to) {
lot.count++
break
}
}
}
我们生成了 10000 个有理数。假若它们在 上均匀地随机分布,那么当我们划分宽度为 的区间时,我们期望每个区间中会落入 个样点。
简单地搞一下卡方检验:
const expect = size * interval_width
const chi_sq =
interval_lots
.map(it => Math.pow(it.count - expect, 2) / expect)
.reduce((x, a) => a + x, 0)
console.log(chi_sq)
// 15.159999999999998
我们划分了 20 个区间,则自由度为 19. 设显著性水平 查卡方表得 ,所以可以认为样点是均匀分布的。
不过,假如我们尝试用这个算法随机生成三维空间中的点的话:
const next = () => {
seed = lcg_randu(seed)
return seed / 0x80000000
}
const point_count = 10000
const points = new Array(point_count)
for (let i = 0; i < point_count; i++) {
points[i] = [next(), next(), next()]
}
我们会得到如下的结果:
SVG 图象,包含 10000 个节点;这些点会落到几个平面中,形成非常有规律的图样
评论
?
参与讨论