我问了一个与此非常类似的问题,所以我将在最后提及以前的解决方案,我有一个website,它在将客户端的CPU存储到服务器的同时使用客户端的CPU计算π,到目前为止,我已经得到了:
'701.766.448.388'指向圆内,总共为'893.547.800.000',这些数字是使用此代码计算的。 (示例见https://jsfiddle.net/d47zwvh5/2/)
let inside = 0;
let size = 500;
for (let i = 0; i < iterations; i++) {
var Xpos = Math.random() * size;
var Ypos = Math.random() * size;
var dist = Math.hypot(Xpos - size / 2, Ypos - size / 2);
if (dist < size / 2) {
inside++;
}
}
问题
(4 * 701.766.448.388)/ 893.547.800.000 = 3,141483638
我们得到的结果是正确的,直到第四个数字4应该为5。
以前的问题:
免责声明
可能只是我已经达到极限,但本次演示使用了100万积分并获得了3.16。考虑到我有大约9000亿我认为这可能更准确。
我确实知道,如果我想计算π,这不是正确的方法,但是我只是想确保一切都正确,所以我希望有人能发现错误或我只需要更多“点”。
编辑:关于数字的不现实性有很多提及,这些提及在哪里正确,我现在将它们更新为正确的。
答案 0 :(得分:2)
您可以轻松地估计应该得到哪种错误(错误条),这就是蒙特卡洛的魅力所在。为此,您必须计算第二个动量并估算方差和标准偏差。好消息是,收集的值将与您的均值相同,因为您只是在1比1后加1。
然后,您可以获取模拟sigma的估计值以及所需值的误差线。抱歉,我对Javascript的了解不够,所以这里的代码在C#中:
using System;
namespace Pi
{
class Program
{
static void Main(string[] args)
{
ulong N = 1_000_000_000UL; // number of samples
var rng = new Random(312345); // RNG
ulong v = 0UL; // collecting mean values here
ulong v2 = 0UL; // collecting squares, should be the same as mean
for (ulong k = 0; k != N; ++k) {
double x = rng.NextDouble();
double y = rng.NextDouble();
var r = (x * x + y * y < 1.0) ? 1UL : 0UL;
v += r;
v2 += r * r;
}
var mean = (double)v / (double)N;
var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL)); // variance
var stdd = Math.Sqrt(varc); // std.dev, should be sqrt(Pi/4 (1-Pi/4))
var errr = stdd / Math.Sqrt(N);
Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");
mean *= 4.0;
errr *= 4.0;
Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");
}
}
}
在获得10个 9 个样本后
Mean = 0.785405665, StdDev = 0.410540627166729, Err = 1.29824345388086E-05
PI (1 sigma) = 3.14157073026184...3.14167458973816
PI (2 sigma) = 3.14151880052369...3.14172651947631
PI (3 sigma) = 3.14146687078553...3.14177844921447
看起来正确。很容易看出,在理想情况下,方差等于(Pi / 4)*(1-Pi / 4)。确实没有必要计算v2
,只需在仿真后将其设置为v
。
坦率地说,我不知道为什么你没有得到预期的结果。求和的精确度损失可能是答案,或者我怀疑,由于播种和重叠序列(因此实际N大大低于900万亿),您的模拟没有生成独立的样本。
但是使用这种方法,您可以控制错误并检查计算的方式。
更新
我已插入您的电话号码,以表明您明显低估了价值。代码
N = 893_547_800_000UL;
v = 701_766_448_388UL;
v2 = v;
var mean = (double)v / (double)N;
var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL));
var stdd = Math.Sqrt(varc); // should be sqrt(Pi/4 (1-Pi/4))
var errr = stdd / Math.Sqrt(N);
Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");
mean *= 4.0;
errr *= 4.0;
Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");
并输出
Mean = 0.785370909522692, StdDev = 0.410564786603016, Err = 4.34332975349809E-07
PI (1 sigma) = 3.14148190075886...3.14148537542267
PI (2 sigma) = 3.14148016342696...3.14148711275457
PI (3 sigma) = 3.14147842609506...3.14148885008647
因此,很明显,您在某处存在问题(代码?表示形式中丢失的准确性?汇总中丢失的准确性?重复/非独立采样?)
答案 1 :(得分:1)
任何FPU操作都会降低您的准确性。为什么不这样做:
let inside = 0;
for (let i = 0; i < iterations; i++)
{
var X = Math.random();
var Y = Math.random();
if ( X*X + Y*Y <= 1.0 ) inside+=4;
}
如果我们探测单位圆的第一象限,则不需要将动态范围更改为size
,并且可以测试以2乘幂的形式消除sqrt
的距离。这些更改将提高精度,并提高速度。
不是JAVASCRIPT编码器,所以我不知道您使用的是哪种数据类型,但是您需要确保不超过其精度。在这种情况下,您需要添加更多的计数器变量以减轻其负担。有关更多信息,请参见:[edit1] integration precision。
由于您的数字很大,所以我敢打赌,您已经越过了边界(应该没有小数部分,并且尾随零也是可疑的),例如32位float
最多只能存储整数
2^23 = 8388608
并且您的698,565,481,000,000
远高于此值,因此即使对该变量执行++
操作也会导致精度损失,并且当指数太大时甚至会停止添加...
在整数上这不是问题,但是一旦您根据内部格式越过边界,该值会绕零或取反...但我怀疑这种情况,因为结果将与PI相去甚远。
>