蒙特卡洛法求圆周率 c语言随机函数,蒙特卡洛方法计算圆周率(C语言实现)

蒙特卡洛方法计算圆周率(C 语言实现) 2018 年 3 月 19 日 目录 1圆周率简介1 2蒙特卡洛法计算圆周率的原理1 3编程实现2 3.1生成随机点 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 3.2第一种方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 3.3第二种方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6 4总结8 1圆周率简介 圆周率是一个在数学及物理学中普遍存在的一个数学常数,一般用希腊字母 π 表示,是精确计算圆 周长、圆面积、球体积等几何形状的关键值。 π 是一个无理数,即无限不循环小数。在日常生活中,通常使用 3.14 代表圆周率去进行近似计算, 而十位小数 3.141592654 足以应付一般的计算,但在某些需要进行精密计算的场合,则需要将 π 取值到 小数点后数百位。 在计算机诞生之前,人们一般使用实验、几何计算以及数学分析的方法手工计算 π 的值,而在计算 机诞生之后,人们则应用计算机编程的方式计算它的值,使得无论在计算精度还是计算速度均达到了前 所未有的高度。 本文将主要应用 C 语言中的选择结构和循环结构知识,并采用蒙特卡洛法编程计算圆周率 π 的近似 值。 2蒙特卡洛法计算圆周率的原理 蒙特卡洛法 (Monte Carlo method) 是一种以概率统计理论为指导一类非常重要的数值计算方法, 因 此也称统计模拟方法。该方法的基本思想是通过“实验” ,以某件事情出现的频率估计为这一随机事件的 概率,并将其作为问题的解。 用蒙特卡洛法求圆周率 π 的基本原理如图 1 所示。 1 (a) n = 100(b) n = 800(c) n = 2000 图 1: 蒙特卡洛法求 π 的原理图 图中,红色的点表示落入半径为 r 的圆内的点,蓝色的点则表示落在圆外但落在该圆外接正方形内 的点,每个点的坐标均为随机生成。称图中的点为“实验点” 。随着实验点的增加,圆的面积 SC与外接 正方形面积 SR的比值接近于红色点数 Nr与总的点数 Nr+ Nb之比,即: SC SR = πr2 (2r)2 ≈ Nr Nr+ Nb 其中,Nr和 Nb分别是红色点和蓝色点的数量,由此可得 π ≈ 4Nr Nr+ Nb (1) 图 2 表明了 π 的近似计算结果与随机生成点的个数 n 之间的关系。可以看出,随着 n 的取值不断 增大,利用公式 (1) 得到的计算结果将逐渐向 π 值趋近,可知当 n → +∞ 时,将有 4Nr Nr+Nb → π。 00.20.40.60.81 104 0 1 2 3 4 5 n π 的近似结果 图 2: π 的近似结果与 n 的关系 3编程实现 在清楚了蒙特卡洛法求圆周率近似值的原理之后,就可以动手编写程序了。不过,在真正编写 π 的 近似求值程序之前,需要首先弄清楚随机点如何生成。 2 3.1生成随机点 在二维平面内,每个点 p 都具有 x 和 y 两个坐标值,随机生成的点的位置是由 x 和 y 的随机性决定 的。在 C 语言中,我们使用 rand 函数生成随机值,该函数已在 stdlib.h 头文件中声明,其函数原型为 int rand(); 该函数返回 0 ~ RAND_MAX 之间的一个随机数。首先,我们编写一个 19 行代码的小程序认识一下这 个函数。 1#include 2#include 3#include 4 5int main() 6{ 7/* 查看 RAND_MAX 的值 */ 8printf("value of RAND_MAX: %d\n", RAND_MAX); 9 10/* 生成随机种子值 */ 11srand(time(0)); 12 13/* 生成 10 个随机数 */ 14for (int i = 0; i < 10; ++i) { 15printf("随机数:%d\n", rand()); 16} 17 18return 0; 19} 代码中用到了 srand、rand 和 time 三个函数,其中 srand 和 rand 函数声明在了头文件 stdlib 中,而 time 函数声明在了头文件 time.h 中,所以代码中的第 2 行和第 3 行即是包含这两个头文件,以 保证对这些函数的正确调用。另外,RAND_MAX 宏也定义在了头文件 stdlib.h 中。 第 8 行代码查看宏 RAND_MAX 的值是多少,这个值代表了 rand 函数返回的最大值。 第 11 行语句中对 srand 函数进行了调用,作用是将当前时间作为随机数发生器的种子值。这样,程 序每次执行时后调用 rand 函数生成的随机数序列都是不同的,保证了程序执行结果的随机性。 第 14 行至第 16 行代码使用 for 循环语句对 rand 函数进行了 10 次调用,对该函数生成的随机值 进行查看。 编译并运行上述程序,得到如下执行结果: value of RAND_MAX: 2147483647 随机数:1809125554 随机数:1374689606 随机数:817374586 随机数:673529929 随机数:1058128761 3 随机数:524710314 随机数:2031481267 随机数:634257328 随机数:811236754 随机数:373071782 你会发现,上述程序的执行结果和你运行的结果并不相同,这是由于 srand 函数的使用保证了每次 程序执行时都使用不同的随机数种子值,如果将程序代码中的第 11 行语句删除,再重新编译并多次运行 程序,会发现什么现象?srand 函数的使用保证了程序执行结果的随机性。 清楚了 rand 函数的使用后,我们来看一下如何将这个函数运用在随机点的生成中。不妨将圆的中心 定义在坐标系的原点,如图 3 所示。 −1.01.0 −1.0 1.0 Ox y 图 3: 随机点映射 由图 3 可知,我们要将随机生成的点的 x 和 y 坐标限定在 [−1.0,1.0] 之间,而 rand 函数返回值的 范围为 [0,RAND_MAX],所以我们使用如下公式进行映射: m = −1.0 + 2.0 r RAND_MAX 其中,r 为 rand 函数的返回值。 使用上述公式,我们就可以使用 rand 函数生成范围为 [−1.0,1.0] 的随机值,将其作为点的随机 x 和 y 坐标,从而得到随机点。 4 3.2第一种方法 首先, 我们来看一下如何使用基本的方法利用蒙特卡洛法计算圆周率的近似值。 这种方法很直观: 输 入总的随机点数 n,然后利用一个 for 循环生成 n 个随机点,并统计落于圆内点的数目,然后利用公 式 (1) 得到结果。流程图可以表示如下: 开始 输入 n i = 0, c = 0 i < n?计算 pi = 4 * c / n 生成 x ∈ [−1.0,1.0] 生成 y ∈ [−1.0,1.0] ++i x*x + y*y < 1.0? ++c 结束 是 是 否 否 图 4: 计算圆周率流程图 据此,编写程序代码如下: 1#include 2#include 3#include 4 5int main() 6{ 7srand(time(0)); 8 9int n, c = 0; 10printf("please input n:"); 11scanf("%d", 5 12 13for (int i = 0; i <= n; ++i) { 14double x = -1.0 + 2.0 * rand() / RAND_MAX; 15double y = -1.0 + 2.0 * rand() / RAND_MAX; 16if (x * x + y * y < 1.0) { 17++c; 18} 19} 20 21double pi = 4.0 * c / n; 22printf("value of pi: %lf\n", double); 23 24return 0; 25} 编译执行程序,得到结果: please input n:100000 value of pi: 3.151480 3.3第二种方法 第二种方法采用迭代法,即将连续两次计算的近似值之差与某一阈值如 1e − 6 进行比较,当这个差 值小于阈值时,即可认为得到了近似的圆周率计算值。 下面首先将示例程序代码附于后,然后对代码进行解释。 1#include 2#include 3#include 4#include 5 6int main() 7{ 8srand(time(0)); 9 10double eps = 1e-6, delta; 11int r = 0, c = 0; 12int n = 0; 13double x, y, p1, p2; 14 15while (n++ < 100) { 16x = -1.0 + 2.0 * rand() / RAND_MAX; 17y = -1.0 + 2.0 * rand() / RAND_MAX; 18if (x * x + y * y < 1.0) { 19++c; 6 20} 21else { 22++r; 23} 24} 25p1 = 4.0 * c / (c + r); 26 27do { 28x = -1.0 + 2.0 * rand() / RAND_MAX; 29y = -1.0 + 2.0 * rand() / RAND_MAX; 30if (x * x + y * y eps); 42 43printf("iterations: %d\n", n - 1); 44printf("delta: %.7lf\n", delta); 45printf("value of pi: %lf\n", p2); 46 47return 0; 48} 上述代码首先需要解释的是第 15 行至第 17 行代码,主要作用是先随机生成 100 个点,并以此计算 的圆周率近似值为迭代的初始值,否则将会出现最开始的两次圆周率近似值之差为 0 的情况,导致得不 到正确的计算结果。 第 27 行至第 41 行的循环代码作用在于随着随机点数的逐渐增加,迭代计算连续的两次圆周率近似 值之差,并与阈值进行比较,如果小于该阈值,则退出循环,将最后计算的一次圆周率近似值作为最终 结果。 编译并运行上述程序,得到如下结果: iterations: 859661 delta: 0.0000010 value of pi: 3.140340 7 4总结 使用控制结构计算圆周率近似值的介绍就到此为止。C 语言初学者应该学会对要解决问题进行分析 的方法,在正式编写程序代码之前,做好一定的研究和分析,这样在编写程序时可以起到事半功率的效 果。 其次,C 编程能力的提高是在长期的编程实践中积累的,C 语言知识的理解和掌握也必须通过长期 的编程实践才能完成,如果大家要想学好 C 语言,就必须注重平时的积累。 8

展开阅读全文

你可能感兴趣的:(蒙特卡洛法求圆周率,c语言随机函数)