用蒙特卡洛方法估算Pi,循环似乎尽早终止

时间:2019-04-05 03:26:58

标签: c++

我正在尝试编写一个程序,该程序通过随机数生成器基于蒙特卡洛方法估算Pi。我正在尝试估计精度为1,2,3,4,5和6位数字的Pi,并将程序打印到屏幕上以使其在Pi的0.1位数之内需要多少点,然后在Pi的.01位数以内一直到Pi的.000001位数。我允许用户输入他们想运行的试验次数,因此它将打印“ Trial 1,2,3,4”等,上面列出了我所有的信息。我被困在最后一点,那就是让它在计算过程中循环返回(它不会打印出比试验1更多的内容)。尽管我没有收到消息程序已终止的消息,所以我无法确定是while循环失败还是嵌套的for循环。请帮忙! :)

我尝试在for循环之间切换,并尝试其他不同的if语句。这是我最接近的操作方式,只是允许用户进行多次试用。

#include "pch.h"
#include <iostream> //need this by default for cin
#include <math.h> //includes math functions
#include <cmath> //includes basic math 
#include <cfloat> //includes floating point numbers
#include <iomanip> //includes setprecision for decimal places
#include <cstdlib> //needed for rand and srand functions
#include <ctime> //needed for time function used to seed generator
using namespace std;

int main()
{
cout << "The purpose of this program is to estimate pi using the monte 
carlo method and a random number generator" << endl << endl;

unsigned seed = time(0);
srand(seed);

float radius;
int trialcount = 0;
int trials;
float accuracy;
const float pi = 3.14159265;
float randpi = 0;
int squarecount = 0;
int circlecount = 0;
float x;
float y;
int n;


cout << "The value of PI can be found as the ratio of areas of a circle of radius r located within a square of side 2r" << endl;
cout << "This program runs a MonteCarlo Simulation that generates numbers located randomly within a square" << endl;
cout << "The count of values within the square and the count of numbers within the circle approximate their areas" << endl;
cout << "An input value of radius determines the size of the circle and square" << endl;
cout << "The user specifies how many trials or test runs are desired" << endl << endl;

cout << "The true value of PI to 8 decimal places is 3.14159265" << endl << endl;

cout << "Input a value for radius: "; 
cin >> radius;
cout << endl;
cout << "How many trials would you like? ";
cin >> trials;
cout << endl << endl;

cout << "Square count gives the Total number of random samples (they are within the square)" << endl;
cout << "Circle count gives the number of random samples that also fall within the circle" << endl << endl;


while (trialcount != trials)
{
    accuracy = .1;
    cout << "Trial " << trialcount + 1 << endl;
    cout << "Accuracy \t\t" << "Square Count \t\t" << "Circle Count \t\t" << "Pi" << endl << endl;

    for (int j = 0; randpi >= pi - accuracy || randpi <= pi + accuracy; j++)
    {
        cout << setprecision(6) << fixed << accuracy << " \t\t" << squarecount << " \t\t" << circlecount << " \t\t" << randpi << endl << endl;
        accuracy = accuracy / 10;

        for (int i = 0; randpi >= pi + accuracy || randpi <= pi - accuracy; i++)
        {
            x = (float)(rand());
            x = (x / 32767) * radius;
            y = (float)(rand());
            y = (y / 32767) * radius;

            squarecount++;

            if ((x * x) + (y * y) <= (radius * radius))
            {
                circlecount++;
            }

            randpi = float(4 * circlecount) / squarecount;

        }
    }

    trialcount++;

}


}

1 个答案:

答案 0 :(得分:1)

我看到的问题:

问题1

第一个for循环没有任何意义。如果要确保使用0.1、0.01、0.001等精度,则只需要一个简单的for循环。应该执行以下操作:

for ( int j = 0; j < 6; ++j )
{
    ...
}

问题2

xy的值计算不正确。您要确保其值小于或等于radius。但是,当您使用时:

x = (float)(rand());
x = (x / 32767) * radius;
y = (float)(rand());
y = (y / 32767) * radius;

不能保证它们小于或等于radius。他们将比radius多得多。您需要使用

x = (float)(rand() % 32768);
x = (x / 32767) * radius;
y = (float)(rand() % 32768);
y = (y / 32767) * radius;

问题3

您需要在内部randpi循环的每次迭代中重置squarecountcirclecountfor的值。否则,您的计算将受到上一次迭代的计算的影响。

外部for循环必须始于:

for (int j = 0; j < 6; j++)
{
   accuracy /= 10;
   randpi = 0;
   squarecount = 0;
   circlecount = 0;

问题4

内部for循环必须限制为只能运行一定次数。如果由于某种原因未达到精度,则要确保不会溢出i。例如:

int stopAt = (INT_MAX >> 8);
for (int i = 0; (randpi >= pi + accuracy || randpi <= pi - accuracy) && i < stopAt; i++)

对于使用32位int(今天在实践中最常见)的计算机,循环运行的次数不得超过0x7FFFFF(十进制8388607)次

这是代码中的核心问题。您的计算有时不会收敛,也无法确保在循环一定次数的迭代后退出。

进一步的改进

程序中不需要radius作为变量。您可以将xy计算为:

x = (float)(rand() % 32768);
x = (x / 32767);
y = (float)(rand() % 32768);
y = (y / 32767);

并更改逻辑以检查这是否是圆内的一个点

if ((x * x) + (y * y) <= 1.0 )

还应该尝试仅在需要它们的作用域中定义变量。这样可以确保您最终不会使用上一次迭代中的陈旧值。

修订程序

以下经修改的程序对我有用。

#include <iostream> //need this by default for cin
#include <math.h> //includes math functions
#include <cmath> //includes basic math 
#include <cfloat> //includes floating point numbers
#include <iomanip> //includes setprecision for decimal places
#include <cstdlib> //needed for rand and srand functions
#include <ctime> //needed for time function used to seed generator
#include <climits> 

using namespace std;

int main()
{
   cout << "The purpose of this program is to estimate pi using the monte "
      "carlo method and a random number generator" << endl << endl;

   unsigned seed = time(0);
   srand(seed);

   int trialcount = 0;
   int trials;
   float accuracy;
   const float pi = 3.14159265;


   cout << "The value of PI can be found as the ratio of areas of a circle of radius r located within a square of side 2r" << endl;
   cout << "This program runs a MonteCarlo Simulation that generates numbers located randomly within a square" << endl;
   cout << "The count of values within the square and the count of numbers within the circle approximate their areas" << endl;
   cout << "An input value of radius determines the size of the circle and square" << endl;
   cout << "The user specifies how many trials or test runs are desired" << endl << endl;

   cout << "The true value of PI to 8 decimal places is 3.14159265" << endl << endl;

   cout << endl;
   cout << "How many trials would you like? ";
   cin >> trials;
   cout << endl << endl;

   cout << "Square count gives the Total number of random samples (they are within the square)" << endl;
   cout << "Circle count gives the number of random samples that also fall within the circle" << endl << endl;


   while (trialcount != trials)
   {
      accuracy = 0.1;
      cout << "Trial " << trialcount + 1 << endl;
      cout << "Accuracy \t\t" << "Square Count \t\t" << "Circle Count \t\t" << "Pi" << endl << endl;

      for (int j = 0; j < 6; j++)
      {
         accuracy /= 10;
         float randpi = 0;
         int squarecount = 0;
         int circlecount = 0;

         int stopAt = (INT_MAX >> 8);
         for (int i = 0; (randpi >= pi + accuracy || randpi <= pi - accuracy) && i < stopAt; i++)
         {
            float x = ((float)(rand() % 32768) / 32767);
            float y = ((float)(rand() % 32768) / 32767);

            squarecount++;

            if ((x * x) + (y * y) <= 1.0 )
            {
               circlecount++;
            }

            randpi = float(4 * circlecount) / squarecount;
         }

         cout << setprecision(8) << fixed << accuracy << " \t\t" << squarecount << " \t\t" << circlecount << " \t\t" << randpi << endl << endl;
      }

      trialcount++;
   }
}

查看它在https://ideone.com/laF27X上的运行情况。