首先,我本来打算使用割圆法,但发现每一小份的三角形的顶角正弦值必须要使用到圆周率pai,就放弃,不符合目的,拿已知去求已知,No!
法一,莱布尼兹公式
利用arctan的泰勒展开,用多项式去拟合。然后又由arctan 1 = π,带入x = 1.
原理:
但有个缺点是收敛的太慢了,需要很大的循环次数。
代码表现:
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
double result, item;
int looptimes,sign = 1;
cout << "please enter the looptimes to decide the precision : ";
cin >> looptimes;
result = 0;
//initialize the variables
for (int i = 0; i <= looptimes; i++)
{
item = 4 * (double)sign / (2 * i + 1);
result += item;
sign = -sign;
}
cout << scientific << setprecision(5) << result << endl;
return 0;
}
代码方面是很简单,就是数列求和。至于去找其他公式,收敛更快,我太太懒了,又搞不懂。
法二,蒙特卡罗方法
原理:
4. 蒙特卡罗方法
蒙特卡罗方法是一种随机数统计方法。通过在一个单位正方形内随机投点,计算落在内切圆内的点的比例来估算 π:
π≈4⋅圆内点数总点数π≈4⋅总点数圆内点数
这个方法利用了概率论的性质来得到 π 的近似值。
1)第一次从尝试。
定义了两个函数,一个用于生成随机点,一个用于算到定点(1,1)的距离。但是使用cmath库里的rand函数误差太大了。
#include<iostream>
#include<cstdlib>
#include<vector>
#include<ctime>
#include<math.h>
#include<iomanip>
using namespace std;
struct Point
{
int x;
int y;
};
vector<Point> generate_random_point(int count, int min_x, int max_, int min_y, int max_y);
double distance(int x, int y, int dotx, int doty);
int main()
{
int looptimes, in_range = 0;
double pai;
vector<Point>test;
srand(static_cast<unsigned int>(time(NULL)));//set the random seed to make sure that these points are random.
cout << "enter a number to decide the loop times" << endl;
cin >> looptimes;
test = generate_random_point(looptimes, 0, 2, 0, 2);
for (int i = 0; i < looptimes; i++)
{
if (distance(test[i].x, test[i].y, 1,1) <= 1) in_range++;
}
pai = (double)in_range / looptimes * 4;
cout << "the pai is " << fixed << setprecision(5) << pai << endl;
return 0;
}
vector<Point> generate_random_point(int count, int min_x, int max_x, int min_y, int max_y)
{
struct Point point;
vector<Point> points;
for (int i = 0; i < count; i++)
{
point.x = min_x + (rand() % (max_x - min_x + 1));
point.y = min_y + (rand() % (max_y - min_y + 1));
points.push_back(point);
}
return points;
}
double distance(int x, int y,int dotx,int doty)
{
double result,square_x,square_y;
square_x = (double)(x - dotx) * (x - dotx);
square_y = (double)(y - doty) * (y - doty);
result = sqrt(square_x + square_y);
return result;
}
改进:
1. 增加样本数量
提高 looptimes
(循环次数),即增加随机点的数量。这将直接提高估算结果的精度,因为更大的样本量通常会使结果更接近真实值。
2. 更合理的坐标范围
在生成随机点时,你的范围是 (0, 2)
,这会生成一个边长为 2 的正方形,但真正的有效区域是边长为 1 的正方形(中心在 (1,1)),并且需要在半径为 1 的圆内。因此,应该调整范围,使其更适合计算。
3. 使用更精确的随机数生成器
C++11 引入了更先进的随机数生成器,使用 std::random_device
和 std::mt19937
可以提高随机数的质量。
#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
#include<random>
using namespace std;
struct Point
{
double x;
double y;
};
double distance(double x, double y)
{
double result = sqrt((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5));
return result;
}
vector<Point> generate_random_point(int count, double min_x, double max_x, double min_y, double max_y)
{
vector<Point> points;
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis_x(min_x,max_x);
uniform_real_distribution<> dis_y(min_y, max_y);
for (int i = 0; i < count; i++)
{
Point point;
point.x = dis_x(gen);
point.y = dis_y(gen);
points.push_back(point);
}
return points;
}
int main()
{
int looptimes, in_range = 0;
double pai;
vector<Point>test;
cout << "enter a number to decide the looptimes (higer for better accuracy) :" << endl;
cin >> looptimes;
//use the function to create points for test
test = generate_random_point(looptimes, 0, 1, 0, 1);
for (const auto& point : test)
{
if (distance(point.x, point.y) <= 0.5) in_range++;
}
pai = (static_cast<double>(in_range) / looptimes) * 4;
cout << "Estimated pai is :" << fixed << setprecision(5) << pai << endl;
return 0;
}
补充相关知识点,对应的随机数生成器与种子
1)
但还是有缺陷,就是要looptimes非常大才能很精确。
果然好算法决定效率与精度。