我基本上坚持了相当简单的问题:
投掷N币并发现,其中有多少人落地
解决方案性能不能取决于N,因此我们不能只调用Math.random() < 0.5
N次。显然,有高斯分布来拯救。
我使用了Box-Muller方法:
function gaussian_random(mean, variance) {
var s;
var x;
var y;
do {
x = Math.random() * 2.0 - 1.0;
y = Math.random() * 2.0 - 1.0;
s = Math.pow(x, 2) + Math.pow(y, 2);
} while ( (s > 1) || (s == 0) );
var gaussian = x * Math.sqrt(-2*Math.log(s)/s);
return mean + gaussian * Math.sqrt(variance);
}
数学说,N币投掷的均值是N/2
而方差是N/4
然后,我进行了测试,将N次硬币投掷M次,给出最小,最大和平均头数。
我比较了幼稚方法(多次Math.random()
)和高斯Box-Muller方法的结果。
有典型的测试结果:
Toss 1000 coins, 10000 times
Straight method:
Elapsed time: 127.330 ms
Minimum: 434
Maximum: 558
Average: 500.0306
Box-Muller method:
Elapsed time: 2.575 ms
Minimum: 438.0112461962819
Maximum: 562.9739632480057
Average: 499.96195358695064
Toss 10 coins, 10000 times
Straight method:
Elapsed time: 2.100 ms
Minimum: 0
Maximum: 10
Average: 5.024
Box-Muller method:
Elapsed time: 2.270 ms
Minimum: -1.1728354576573263
Maximum: 11.169478925333504
Average: 5.010078819562535
正如我们在N = 1000
上看到的那样,它几乎完全适合。
但是,N = 10
Box-Muller变得疯狂:它允许这样的测试结果,我可以得到(很少,但很有可能)10个硬币投掷11.17头! :)
毫无疑问,我做错了什么。但具体到底是什么?
有source of test,并且链接到launch it
更新了x2:似乎,以前我没有很好地描述问题。它有一般版本:
如何在摊销的常数时间内获得 N N 均匀随机值(离散或连续)的样本均值。高斯分布对于大 N 是有效的,但有没有办法使它在小 N 上工作良好?或者 N ,解决方案应该从高斯方法切换到其他方法(例如直接)。
答案 0 :(得分:2)
数学说,N币投掷的平均值是N / 2,方差是N / 4.
数学只说如果它是一个公平的硬币。并且没有依赖于N的解决方案。
假设所有投掷都是相互独立的,对于确切的行为,使用二项分布而不是正态分布。二项式有两个参数:N是掷硬币的数量,p是获得头部的概率(如果你愿意,则是尾部的概率)。在伪代码......
function binomial(n, p) {
counter = 0
successes = 0
while counter < n {
if Math.random() <= p
successes += 1
counter += 1
}
return successes
}
对于大N有更快的算法,但这很简单,数学上也是正确的。
答案 1 :(得分:0)
根据approved answer中讨论的内容,我找到了这个特定的解决方案。
有经验法则n*p >= 10 and n*(1-p) >= 10
,但我们要定义更严格的规则。
首先,Box-Muller将被限制在[-6,6],以确保正确的结果( 640 kB应该...,我的意思是,6 sigmas应该足够每个人)。
然后,使用6
常量,我们声明,为了使Box-Muller生成有效结果,Math.sqrt(variance) * 6
不得超过mean
。在分别使用N/2
和N/4
作为mean
和variance
以及减少后,我们最终会这样做:
Math.sqrt(N) * 6 <= N
N >= 36
虽然这个条件属实,但我们可以安全地使用上限Box-Muller Gaussian。 在任何较低的样本量,坚持二项式解决方案。
在统计上检查了这条规则 - 在相对较大数量(1000万)的测试中,最小值会从样本大小32及以上的范围内停止。