浦丰投针问题(求圆周率的近似值)—— C++模拟

目录

前言

一、问题介绍

二、公式推导 

三、圆周率的近似值计算 

总结


前言

  最近我在哔哩哔哩上听宋浩老师讲解概率论与数理统计的知识, 被宋老师讲的浦丰投针问题所吸引了,不由得感叹数学的魅力与神奇

 概率论与数理统计: 几何概型 (包含浦丰投针问题)

  浦丰投针问题属于几何概型,我们能够通过模拟投针来计算出圆周率的近似值。那么,究竟什么是浦丰投针问题呢?这个圆周率的近似值又是怎么求出来的呢?下面我们慢慢道来。

一、问题介绍

  浦丰投针问题,也称为蒙特卡罗的投针问题,是由法国数学家乔治·浦丰(Georges-Louis Leclerc, Comte de Buffon)于18世纪提出的一个经典概率问题。该问题是以一条等距离的平行线组成的地板为背景,然后在地板上随机撒下一根长度为l(l ≤ d,其中d为平行线之间的距离)的针。问题的目标是计算出这根针与平行线交叉的概率。解决这个问题的关键是找到一个适当的模型来描述针的位置和方向的随机性。通过分析,可以得到下述公式(其中,P表示针与平行线相交的概率):

P = (2 * l) / (d * π)

  那么,这个公式是如何推导出来的呢?下面我们介绍一种推导方法。 

二、公式推导 

  我们取针的中点,设x为针中点离最近线的距离 (0<=x<=d/2),c为针与线之间的夹角(0度到180度)

  什么情况下针与线相交呢?根据示意图,当0<=x/sinc<=l/2时,针与线相交 。将这个式子整理一下,有 0<=x<=l*sinc/2 。而对于所有的情况(既有相交也有不相交),有0<=x<=d/2

  我们将角度转换成弧度制之后,可以将x与c的关系在一个直角坐标系中表示出来,如图所示:

   其中,整个矩形的面积表示所有的情况,用红笔标记的面积代表的是针与线相交的情况。因此,针与线相交的概率为红笔标记的面积比上整个矩形的面积,这就要运用到高等数学中的定积分知识了,具体运算如下图所示:

  因此,我们就推导出来了前面所写的公式 。如果大家看到这里还不是很理解,也可以查看我在前言部分放上的链接,聆听宋浩老师的热情讲解。

三、圆周率的近似值计算 

  我们知道,当我们试验的次数足够多时,针与线相交的次数与试验的总次数的比值就越趋近于我们利用数学知识计算出来的概率 。我们假设针与线相交的次数为n,试验的总次数为N。那么,我们就有:P = (2 * l) / (d * π)= n / N 。将这个式子整理一下就可以得到求解π近似值的式子:

π = ( 2 * l * N ) / ( n * d )

  基于上面的推论,我们就可以写一个C++程序来模拟投针,进而计算出π近似值。在程序中,每次试验时我们都需要随机生成一个距离x,一个角度c,再通过数值的比较确定其是否与线相交。具体代码如下:

#include<iostream>
using namespace std;
#include<math.h>
#include<time.h>
#include<random>

long long int l;								 //针的长度
long long int d;								 //两条线之间的距离

#define Pi 3.1415926							 //由于计算sin值需要将角度转成弧度进行计算,所以我这里先定义了Pi的近似值

int main() {
	unsigned long long int N = 1000000000;		 //试验次数  10亿次
	unsigned long long int n = 0;				 //成功次数
	cout << "请输入两条线之间的距离:"; cin >> d;
	table:
	cout << "请输入针的长度:"; cin >> l;
	if (l > d) {
		cout << "针的长度不能大于两条线之间的距离" << endl;
		goto table;
	}
	default_random_engine e(time(0));			//随机数引擎
	uniform_real_distribution<double> u(0.0, d/2.0);
	uniform_real_distribution<double> v(0.0, 180.0);
	for (unsigned long long int i = 0; i < N; i++) {
		double x = u(e);						//随机的距离
		double c = v(e);						//随机的角度
		if (x <= l * sin(c * Pi / 180) / 2.0) {
			n++;
		}
	}
	double pi = (2.0 * l * N) /(n * d);
	cout << pi << endl;
	return 0;
}

  这里我采用的是随机数引擎来生成随机的小数,如果大家不是很理解的话,可以查看下面这篇博客:

C++生成指定范围的随机小数

  我让程序帮我模拟了10亿次投针,每次运行程序都要运行好几分钟才能算出最终的结果,下面是程序运行得出来的一个近似值:

总结

  可以看到,随着模拟次数的增大,结果越接近 π 。不过,由于计算机对于小数的处理方式,误差肯定是不可避免的,比如说究竟保留多少位小数等等。本篇博客到这里就结束了,如果您觉得还不错,不妨点个免费的赞再走吧!

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

边城仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值