问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

用随机函数计算圆周率π:蒙特卡罗方法详解

创作时间:
作者:
@小白创作中心

用随机函数计算圆周率π:蒙特卡罗方法详解

引用
CSDN
1.
https://blog.csdn.net/qq_37756660/article/details/137351803

在编程的世界里,随机函数是一个神奇的存在。它不仅能帮助我们完成各种有趣的实验,还能用来计算圆周率π的值。本文将带你走进随机函数的世界,通过一个简单的实验,让你了解如何用编程计算π的值。

今日任务

圆周率π对我们来说肯定不是一个陌生的概念,它指的是圆的周长与直径的比值。在古代,数学家们为了计算π的精确值想尽方法,可能穷尽一生也不过精确到小数点后几位而已。但到了现在,你可能不相信,只要你知道π的定义,就可以利用编程轻易计算出π的值。那究竟怎么做到呢?

我们先来看一个用蒙特卡罗法计算π的示意图:


图1:蒙特卡罗法示意图

通过观察图1,请思考一个问题:如果你随机地在正方形区域中选择一个点,那么这个被选择的点,也恰巧落在圆形红色区域的概率是多大?这个问题很简单,就是圆面积和正方形面积的比值,简单计算就可以得到这个概率值,应该是π/4。

也就是说,如果我们做大量的随机实验,最终落在圆内部的次数除以总次数再乘以4得到的值,应该接近圆周率π。随机次数越多,所得到的数值越接近π。你肯定不喜欢做这种重复的“重体力”劳动,但如果你写好编程,让它帮你做这件事,那就简单容易快捷多了。计算机可是一个不怕辛苦、没有怨言的好帮手,今天就让它来帮助我们完成这个任务吧。

必知必会,查缺补漏

思考一下,其实要完成上面这个任务,我们已经具备了一些基础知识,比如说:分支结构(if…else)可以帮助你判断某个点是否在圆内部,循环结构(for/while)可以帮助你完成大量的重复实验。

说到随机,就需要说一下真随机与伪随机的概念了。

所谓真随机其实并不难理解,我们以掷骰子为例,掷出1~6点的概率均为1/6,如果我问你,上一次掷出的点数是4,那么下一次掷出6点的概率是多大?你会发现,依然是1/6,我们称这两次掷骰子的事件是相互独立的,上一次的结果和下一次之间没有必然联系。


图2:真随机示意图

通过上面这个示意图,你就很容易看出,所谓真随机,就是我们通常意义下的随机。那么什么又是伪随机呢?从名字上面来看,伪随机,带个伪字,说明本质上不是随机,可看起来是随机。

下面请你注意观察下图的两个数字循环序列:


图3:显然规则与非显然规则

你观察上面这两个数字序列,会发现,第一个序列是123456,这是一个有明显规律的序列,你一定不会觉得这个序列是随机生成的。另一个序列是421635,好像没有什么明显的规律,相比于第一个序列,你是不是更偏向于相信第二个序列是随机生成的序列呢?

第二个序列就是我刚刚所说的伪随机,看起来像是随机序列,可实际上,4后面一定会出现2,2后面一定是1,1后面一定是6,也就是说前一个数字决定了后一个数字。

计算机中究竟如何制造出来这样一个伪随机序列呢,这个问题留到后面的 “动手搞事情” 中,我会使用一行简单的数学公式,制造一个包含100个数字的伪随机数字序列,类似于上图中第二个序列的加大版。

最后你会发现,所谓计算机中的伪随机数序列,就是类似第二个序列那样的,没有什么明显规律的一个规模更大的循环序列

现在你知道为什么叫做伪随机了吧,那是因为,一旦要是上一个随机函数的值确定了,下一个数字也就确定了,而纯正意义上的真随机,应该是前后两次出现的数字是两次独立事件,没有任何关系。

现在我们所接触到的语言中,没有真随机,全是伪随机。也就是说,语言中给我们准备好了一个随机函数,这个随机函数会根据上一个随机值和一个固定的计算规则,得到下一个随机值

而你在其他资料中可能会看到随机种子这个概念,设置随机种子就是在设置随机函数中记录的上一个随机值。例如,上面我们自己做出来的6个长度的伪随机序列,如果随机种子设置为值1,我们得到的值依次是635421,如果设置为值3,那么我们将依次得到542163。

下面就看看C语言中的随机函数的用法吧:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
    printf("%d\n", rand() % 1000); // 永远输出固定值
    srand(time(0));
    printf("%d\n", rand() % 1000); // 每次运行都不同
    return 0;
}

上面代码中,我们用rand()函数,获得一个随机值,这个就是我们前面讲的随机函数,它将依次的返回随机序列中的每一个值。

而srand()函数就是设置随机种子的函数,也就是设置随机函数上一次的状态值。time(0)将返回一个时间戳,你就可以把他当成和当前时间相关的一个整型数字。

你会发现,上面这段程序中,在第6行代码里,我们虽然使用了rand()函数,可每次运行都将输出同样的值,这是因为我们没有设置随机种子,每次运行时rand()函数所记录的起始值都相同,所以每次运行输出的随机值也都相同。

而第8行代码中,由于我们根据程序运行时的当前时间设置了随机种子,每次运行程序,第8行都将输出不同的值。事实上,如果你在srand()函数里面设置一个固定值,每次运行程序,结果也都将是一样的,这个你可以自行尝试。

至此,我们就准备好了今天任务的全部基础知识了,接下来做道练习题,锻炼一下吧。

一起动手,搞事情

思考题:设计迷你随机函数

设计一个循环过程,循环100次,以不太明显的规律输出1~100中的每个数字。

要求1:规律尽量不明显。

要求2:只能使用循环和最基本的运算,不允许超前使用数组。

下表是我的程序输出的序列,以供你做参考:

  5 15 45 34  1  3  9 27 81 41
 22 66 97 89 65 94 80 38 13 39
 16 48 43 28 84 50 49 46 37 10
 30 90 68  2  6 18 54 61 82 44
 31 93 77 29 87 59 76 26 78 32
 96 86 56 67100 98 92 74 20 60
 79 35  4 12 36  7 21 63 88 62
 85 53 58 73 17 51 52 55 64 91
 71 11 33 99 95 83 47 40 19 57
 70  8 24 72 14 42 25 75 23 69  

用有趣的方法计算π值

准备完了所有的基础技能后,就让我们来完成开始说的那个任务吧。

我们来思考一下哈,首先我们需要有一个循环,循环每一次,让计算机帮我们做一次实验。每次实验呢,让计算机模拟随机选择点的这个过程,然后我们需要判断一下随机选择的点,是否在圆内部;如果在,我们就记录一次。最后用落在圆里的次数比上总实验次数再乘以4,就得到了π的近似值。

这个过程中,你到现在还比较懵的,可能就是随机选点的过程了。那就跟我来看下面代码吧:

double x = 1.0 * rand() / RAND_MAX;

上述代码中的rand()随机函数,返回值的范围是[0, RAND_MAX],通过上述表达式计算,我们就得到了一个[0.0, 1.0]之间的随机值了。

下面就让我们完善程序:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
    int n = 0, m = 0;
    for (int i = 0; i < 1000000; i++) {
        double x = 1.0 * rand() / RAND_MAX;
        double y = 1.0 * rand() / RAND_MAX;
        if (x * x + y * y <= 1.0) m += 1;
        n += 1;
    }
    printf("%lf\n", 4.0 * m / n);    
    return 0;
}

上述代码中,我让计算机重复10万次实验,每次在坐标轴的第一象限中的1 * 1的区域中随机选择一个点,变量m记录的是落在圆内部的次数,变量n记录的是总实验次数。运行这个程序,在我的环境中,输出的是3.142096,你可以试一下在你的环境中的运行结果,以及加大实验次数以后,对结果的影响。

是不是很难想象,如果没有计算机,我们自己将如何来完成这10万次实验呢?想想都是很痛苦的过程!

课程小结

今天这节课,你了解了C语言中的随机函数,以及计算机中随机函数的基本原理。最后呢,总结一下今天的重点,就两点:

  1. 计算机中都是伪随机函数,也就是说,下一次的随机值,跟本次的随机值是相关的。
  2. 使用srand函数设置随机种子,也就是设置伪随机过程中的第一个起始点的位置。

理解了上面这两点,也就算是真正理解了计算机中的随机函数的概念了。从今天开始,记住,计算机就是你的小帮手了,以后的日子里,请动用你的智力,使用它的体力!随着你的思维逻辑越来越严谨,你会爱上这个帮手的,即使它日后可能会因为一些不知名的小 Bug 惹你不开心,相信我,都是暂时的。

本文介绍了使用随机函数计算圆周率π的方法。首先介绍了π的定义和古代数学家们为了计算π的精确值所做的努力。随后引入了蒙特卡罗法,通过大量的随机实验来计算π的值。文章还介绍了真随机和伪随机的概念,以及在C语言中使用随机函数的方法。最后,提出了一个思考题,要求设计一个迷你随机函数,循环输出1~100中的每个数字,但要求输出的规律不太明显。通过本文的介绍,读者可以了解到随机函数的概念和在计算π值中的应用,以及如何在程序中使用随机函数进行随机数生成。文章内容生动有趣,通过实例代码展示了随机函数的应用,对读者进行了技术知识的传授和思维能力的锻炼。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号