随机数生成其实挺困难的

(伪)随机数生成其实还挺复杂的,因为你可能会搞出一个看似均匀而随机的分布,但是在高维度上会出问题的算法。考虑这样一个 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 个节点;这些点会落到几个平面中,形成非常有规律的图样

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论