提问人:Domino 提问时间:5/5/2020 更新时间:5/6/2020 访问量:513
生成 BigInt 值的随机样本
Generating a random sample of BigInt values
问:
对于用 JavaScript 实现的游戏,我需要生成一个唯一数字的随机列表,其范围可能大于 。这带来了三个重要挑战:n
[0, N)
N
Number.MAX_SAFE_INTEGER
- 内存成本不能是 O(N),因为我们没有足够的 RAM。这意味着我们不能使用修改后的 Fisher-Yates 随机算法从元素数组中获取随机值。(JS 数组仅限于 32 位索引,但在此之前我们早就耗尽了 RAM。
n
N
- 执行时间应尽可能接近 O(n) 而不是 O(N)。我预计 n 会相对较小(< 100)。
- 大多数数学运算使用双精度浮点值,不适用于这么大的数字。
我在 Kim-Hung Li 的储层采样算法 L 中找到了解决前两个挑战的解决方案。总而言之,这个算法背后的想法是,我们洗牌第一个数字以形成一个初始水库,然后我们用从范围的其余部分获取的随机数逐步覆盖其中的一些数字。我们使用几何级数来确定每次迭代要跳过多少个数字。n
假设 sampleSize 是一个数字,populationSize 是一个 BigInt,代码如下:
// Creates an array initialized with [0n, 1n, ... BigInt(count-1)].
function bigRange (count) {
const array = Array(count);
for (let i = 0; i < count; i += 1) {
array[i] = BigInt(i);
}
return array;
}
function bigSample (sampleSize, populationSize) {
const reservoir = shuffle(bigRange(sampleSize));
let record = BigInt(sampleSize);
let weight = exp(log(random()) / sampleSize);
while (true) {
const skipCount = floor(log(random()) / log(1 - weight));
record += BigInt(skipCount);
if (record >= populationSize) {
return reservoir;
}
reservoir[floor(random() * sampleSize)] = record;
record += BigInt(1);
weight *= exp(log(random()) / sampleSize);
}
}
但是,此代码不适用于任意大的数字,因为浮点数的精度是有限的。随着 populationSize 的增长,浮点数的非有效位最终被使用,我们不再具有均匀分布,然后我们进入不安全的整数区域,最后有可能成为 .知道了这一点,我就想知道......skipCount
Infinity
- 在什么时候,此功能变得不可靠?
- 我有没有办法改进这个算法来弥补 JavaScript 的局限性?
- 有没有更好的选择?
请注意,我对概率的了解是有限的,我花了一周时间挖掘我几乎不理解的科学论文后编写了这段代码。我确实有另一种方法,但它的效率要低得多,并且需要将随机数生成与拼图生成逻辑混合在一起。
答:
如果相对较小,另一种方法是将已生成的数字存储在 JavaScript Map 或普通 JavaScript 对象中,其中键是数字(但请注意,Maps 和普通对象都会将这些键转换为字符串,因此在这种情况下,键应该与值相同)。如果远小于存储每个新随机数的时间,则接近恒定时间复杂度,并且所有随机数在第一次尝试时都不同的概率接近 1。n
n
N
n
N
- 我写了一节,展示了根据大小和数量对物品进行抽样的各种方法。
n
N
n
N
- 但是,如果你试图从大量可能的值(例如,100 位数字)中生成唯一的随机数,并且这些数字是为了以某种方式识别某些东西,那么有几件事需要记住,我在“唯一随机标识符”中详细介绍了这一点。
n
N
评论
n/N
n
N
n
N
Math.random
您可以一点一点地生成值并将它们添加到尝试中。当您生成的不是第一个数字时,您可以使用尝试分支中的可用空间计数来确定进入每个分支的概率。如果浮点运算是完美的,这将从均匀分布中给出唯一的数字,但我想这些在尝试中高水平概率的不一致不会产生显着差异(因此,这需要进一步研究)。请注意,如果您生成了一个大于 N 的数字,则只需重新开始生成过程。如果您不制作不必要的比特,则再生的概率将小于 50%,并且额外几代的期望值为 1。
评论
N=a**b
a
b
b
a
评论