Вычисление π с использованием ограничений моделирования Монте-Карло

Я задал вопрос, очень похожий на этот, поэтому в конце я упомяну предыдущие решения, у меня есть веб-сайт, который вычисляет π с помощью процессора клиента, сохраняя его на сервере, пока что у меня есть:

«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.

Предыдущие проблемы:

  1. Я ошибся в расчете расстояния.
  2. Я разместил круги от 0 до 499, которые должны быть от 0 до 500.
  3. Я не использовал float, что уменьшило «разрешение».

Отказ от ответственности

Возможно, я просто достиг предела, но эта демонстрация использовала 1 миллион баллов и получила 3,16. учитывая, что у меня есть около 900 миллиардов, я думаю, что это может быть точнее.

Я понимаю, что если я хочу вычислить π, это неправильный способ сделать это, но я просто хочу убедиться, что все правильно, поэтому я надеялся, что кто-нибудь заметит что-то не так, или мне просто нужно больше «точек». '.

РЕДАКТИРОВАТЬ: есть довольно много упоминаний о том, насколько нереалистичны цифры, где эти упоминания правильны, и теперь я обновил их, чтобы они были правильными.


person Schotsl    schedule 12.09.2018    source источник
comment
1) Эти числа невероятно велики (особенно для Javascript)... 2) Они также подозрительно округлены до ближайшего миллиона... 3) Math.random() может быть не лучшим генератором случайных чисел.   -  person meowgoesthedog    schedule 12.09.2018
comment
@meowgoesthedog Вы были правы, они отображаются неправильно (по этой же причине они округлены). Я обновил его точными цифрами из базы данных. Я попытаюсь использовать более безопасный генератор случайных чисел, хотя это повлияет на скорость :(   -  person Schotsl    schedule 12.09.2018
comment
Ошибка составляет -0,00011, но каково стандартное отклонение оценки?   -  person Yves Daoust    schedule 12.09.2018
comment
Что такое size? В любом случае ошибка оценки примерно пропорциональна величине, обратной квадратному корню из размера выборки. Когда вы переходите от 1 миллиона к 1 триллиону, размер выборки увеличивается в миллион раз, но только в тысячу раз увеличивается квадратный корень из размера выборки. Который -- должен купить вам еще 2 или 3 знака после запятой точности. Это не означает, что в коде нет ошибки, но то, что вы видите, может быть не таким удивительным, как вы думаете.   -  person John Coleman    schedule 13.09.2018
comment
Размер @JohnColeman — это переменная, которая определяет размер круга и квадрата, а также ограничение случайных чисел. Однако спасибо за объяснение, возможно, я просто достиг практического предела вычислительной мощности, имеющейся в моем распоряжении.   -  person Schotsl    schedule 14.09.2018
comment
@YvesDaoust Я лично не знаю, как я могу рассчитать стандартное отклонение, но ответ Северина Паппаде, кажется, дает некоторое представление об этом.   -  person Schotsl    schedule 14.09.2018


Ответы (2)


Вы можете легко оценить, какую ошибку (планки погрешностей) вы должны получить, в этом вся прелесть Монте-Карло. Для этого вам нужно вычислить второй импульс и оценить дисперсию и стандартное отклонение. Хорошо, что собранное значение будет таким же, как и среднее, потому что вы просто сложили 1 после 1 после 1.

Затем вы можете получить оценку сигмы моделирования и планки погрешностей для желаемого значения. Извините, я недостаточно знаю 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}");
        }
    }
}

После 109 образцов я получил

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

Итак, у вас явно есть проблема (код? Потеря точности в представлении? Потеря точности при суммировании? Повторная/независимая выборка?)

person Severin Pappadeux    schedule 13.09.2018
comment
Я не очень хорошо знаю C#, так что это тяжело для нас обоих! В любом случае, указанное число в 900 триллионов было неправильным (см. редактирование), на самом деле это 900 миллиардов, что может объяснить разницу в ошибках? - person Schotsl; 14.09.2018
comment
@Schotssl `так тяжело нам обоим!` Ага! Насчет 900 миллиардов, я вставил ваши цифры в код оценки ошибок, пожалуйста, проверьте обновление. Нет, у тебя где-то реальная проблема. - person Severin Pappadeux; 14.09.2018
comment
Просматривая это, я действительно не мог найти никаких проблем, я попробую использовать более безопасный генератор случайных чисел и посмотрю, к чему это приведет. - person Schotsl; 16.09.2018
comment
@Schotssl, пожалуйста, держите нас в курсе - мне любопытно, в чем может быть проблема, потому что это не очевидно неправильно, а лишь немного - person Severin Pappadeux; 17.09.2018
comment
Прошло некоторое время, но я, наконец, обновил код, чтобы использовать крипто-рандом, так что теперь мы ждем! - person Schotsl; 08.10.2018
comment
@Schotsl Отлично, жду результатов - person Severin Pappadeux; 09.10.2018

любая операция 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] точность интеграции.

Поскольку ваши числа довольно большие, держу пари, вы уже пересекли границу (не должно быть дробной части, а конечные нули также подозрительны). Например, 32-битный float может хранить только целые числа до

2^23 = 8388608

и ваш 698,565,481,000,000 намного выше этого, поэтому даже операция ++ с такой переменной приведет к потере точности, а когда показатель степени слишком велик, он даже перестанет добавлять...

Для целых чисел это не проблема, но как только вы пересекаете границу в зависимости от внутреннего формата, значение оборачивается вокруг нуля или отрицается ... Но я сомневаюсь, что это так, поскольку тогда результат будет далек от PI.

person Spektre    schedule 13.09.2018
comment
При изменении Javascript с помощью вашего нового фрагмента я понимаю, почему вычисление расстояния является улучшением скорости, но единственное преимущество добавления 4 к 1 заключается в том, что вы правильно вычисляете π? Так как в этот момент вы можете просто разделить внутренние и внешние числа. Ваш код действительно сильно увеличивает скорость, просто глядя на него, кажется, что это трехкратное увеличение! и ранее я дал неправильные числа (см. редактирование), но текущие числа должны нормально работать в Javascript. - person Schotsl; 14.09.2018
comment
@Schotsl Да, вы можете делить на 4 в последнем делении вместо +=4, что также может сэкономить вам 2 бита в счетчике, если вы находитесь рядом с границей переменной битовой ширины. Поскольку одно деление не имеет значения с точки зрения производительности. Мне просто было лень писать еще одну строку кода... Но основное повышение точности заключается в том, чтобы избежать ненужных вычислений с числами с плавающей запятой. - person Spektre; 14.09.2018