不断从[0,1]中随机选择一个数进行累加,直到其和超过1,需要选择几次?

微信搜索:编程笔记本
获取更多校招干货知识

今天我们来看一个有趣的问题:
不断从[0,1]中随机选择一个数进行累加,直到其和超过1,需要选择几次?

对于一次特定的实验,次数是一个确定的整数。我们要讨论的是,选择次数的数学期望。或者说,平均需要多少次。

现在,这已经变成了一个比较复杂的数学问题了。但是,好在问题描述并不复杂,我们可以用计算机进行数值模拟。

程序流程图:

step1:s=0;
step2:[0,1]中随机选择一个数x;
step3: s=s+x;
step4:s>1,停止;否则转到step2

参考c++代码:

#include <stdio.h>
#include <random>
#include<iomanip>

int main()
{
    // 设置随机数生成器
    std::default_random_engine e(time(0));
    std::uniform_real_distribution<double> gen(0.0, 1.0);
    
    double sum = 0.0;
    int cnt = 0;
    
    // 总试验次数与总选择次数
    int times = 100;
    long long all = 0;
    
    for (int i = 0; i < times; ++i) {
        sum = 0;
        cnt = 0;
        
        while (sum <= 1.0) {
            sum += gen(e);
            ++cnt;
        }
        
        all += cnt;
    }
    
	// 选择次数期望 = 总选择次数 / 总试验次数
    double exp = double(all) / double(times);
    printf("exp = %.5lf\n", exp);
}

下面我们运行程序:

jincheng@DESKTOP-42T69DJ:/mnt/e/LinuxSubSysFile$ g++ test.cpp
jincheng@DESKTOP-42T69DJ:/mnt/e/LinuxSubSysFile$ ./a.out
exp = 2.74012

微信搜索:编程笔记本
获取更多校招干货知识

从实验结果可以看到,当我们进行100次这样的实验,得到的平均选择次数为2.74012
当然了,既然是验证性实验,100次还是太少了,下面我们修改times变量值,增加试验次数,再来观察实验结果。

  • times = 1000
    exp = 2.72800
  • times = 10000
    exp = 2.71440
  • times = 100000
    exp = 2.71601
  • times = 1000000
    exp = 2.71939
  • times = 10000000
    exp = 2.71833
  • times = 100000000
    exp = 2.71824

随着实验的进行,我们似乎看出来某种规律,选择次数的期望值怎么这么眼熟啊…是不是有点像**自然对数底数e**啊?

我们知道,自然对数底数e = 2.71828 18284 59……。当我们进行足够多次实验(100000000次)时,得到的选择次数期望值为2.71824,这与e的前四位小数惊人般地雷同!直觉告诉我,这一定不是巧合!此事必有蹊跷!

先说结论:

不断从[0,1]中随机选择一个数进行累加,直到其和超过1,需要的选择次数的期望值为欧拉常数e

今天的编程小知识主要是C++11新标准的随机数生成库的应用。在新标准下使用随机数,原则上我们不再使用rand()srand()函数了,而是使用随机度更高、效率更快的随机数生成库,它内置了一系列常用的随机分布。感兴趣的小伙伴可以自行查阅学习。下面是上述结论的严格数学证明,请小伙伴们有选择性地阅读。


求数学期望,就是随机量的所有可能取值*随机量取该值的概率

在我们讨论的问题中,选择次数(记为C)的可能取值为:2,3,4,...,则:

E ( C ) = Σ n = 2 ∞ [ n ∗ P ( C = n ) ] E(C) = \Sigma_{n=2}^{\infty} [n*P(C=n)] E(C)=Σn=2[nP(C=n)]

下面我们从简单的情况入手。

  • 考虑n = 2
    等价于,在边长为 1 的正方形中取一点,其落在箭头区域(x + y \> 1)的概率,即P = 1/2 = 1 - 1 / (2!)
  • 考虑n = 3
    等价于,在棱长为 1 的正方体中取一点,其落在箭头区域(x + y + z \> 1)的概率,即P = 5/6 = 1 - 1 / (3!)

为了不让证明过程过于复杂,我们直接给出一个数学定理:n维单位空间中,其中任取一点,落在单位之外的概率为 P = 1 - 1 / (n!)

在我们的问题中,考虑“第n次选择后,其和恰好大于 1 ”的事件。该事件成立的前提是,前 n-1 的和并未大于 1 ,而第 n 次选择之后,其和大于 1 。则有:

P ( C = n ) = [ 1 − 1 / ( n ! ) ] − [ 1 − 1 / ( ( n − 1 ) ! ) ] P(C=n) = [1 - 1 / (n!)] - [1 - 1 / ((n-1)!)] P(C=n)=[11/(n!)][11/((n1)!)]

上式中后半部分减去的是“前n-1次选择后和大于 1 ”的概率。如此一来,我们的问题便能够求解了。

E ( C ) = Σ n = 2 ∞ [ n ∗ P ( C = n ) ]                                                   = Σ n = 2 ∞ n ∗ { [ 1 − 1 / ( n ! ) ] − [ 1 − 1 / ( ( n − 1 ) ! ) ] } = Σ n = 2 ∞ n ∗ { 1 / ( ( n − 1 ) ! ) − 1 / ( n ! ) }                    = Σ n = 2 ∞ n ∗ { ( n − 1 ) / ( n ! ) }                                  = Σ n = 2 ∞ { 1 / ( ( n − 2 ) ! ) }                                        = Σ n = 0 ∞ { 1 / ( n ! ) }                                                   E(C) = \Sigma_{n=2}^{\infty} [n*P(C=n)]\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ =\Sigma_{n=2}^{\infty} n*\{[1 - 1 / (n!)] - [1 - 1 / ((n-1)!)]\}\\ =\Sigma_{n=2}^{\infty} n*\{1 / ((n-1)!) - 1 / (n!)\}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ =\Sigma_{n=2}^{\infty} n*\{(n - 1) / (n!)\}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ =\Sigma_{n=2}^{\infty} \{1 / ((n - 2)!)\}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ =\Sigma_{n=0}^{\infty} \{1 / (n!)\}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ E(C)=Σn=2[nP(C=n)]                                                 =Σn=2n{[11/(n!)][11/((n1)!)]}=Σn=2n{1/((n1)!)1/(n!)}                  =Σn=2n{(n1)/(n!)}                                =Σn=2{1/((n2)!)}                                      =Σn=0{1/(n!)}                                                 

由泰勒级数我们知道, Σ n = 0 ∞ { 1 / ( n ! ) } \Sigma_{n=0}^{\infty} \{1 / (n!)\} Σn=0{1/(n!)} = e
证毕。

微信搜索:编程笔记本
获取更多校招干货知识

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用循环来实现从1到n的累加,具体代码如下: ``` def sum_n(n): result = for i in range(1, n+1): result += i return result ``` 其,range(1, n+1)表示从1到n的整序列,循环遍历每个累加到result变量,最后返回累加结果。 ### 回答2: 目要求编写一个来实现从1到n共n个累加,这个目看上去比较简单,但是,需要按照目要求来编写,需要注意的细节还是有很多的。 首先,需要清楚要编写一个什么样的函。这个函需要接收一个n,表示从1到n共n个需要累加。然后,这个函需要返回累加结果。 接下来,我们需要考虑如何来实现这个函。常用的方法有两种,一种是递归,一种是循环。 递归方法是将从1到n共n个累加,转化为把前n-1个累加和加上第n个的问。代码如下: ``` int add(int n){ if(n == 1) return 1; //1到1的和是1 else return n+add(n-1); //1到n的和是1到n-1的和再加上n } ``` 循环方法是从1到n循环累加。代码如下: ``` int add(int n){ int sum = 0; for(int i=1;i<=n;i++){ sum += i; } return sum; } ``` 两种方法各有优缺点。递归方法看起来比较简洁,但是需要注意递归深度过大可能导致栈溢出。循环方法则比较直接,但是需要定义一个变量来存储累加和。 最后,我们需要注意的是,对于一些很大的n值,可能会溢出。因此,在程序需要对这个问进行保护,例如在开发阶段进行预估,或者对于溢出的情况进行特判,等等。 总的来说,编写一个从1到n共n个累加,看似简单,但需要注意很多细节。只有认真思考并仔细处理这些细节,才能编写出稳定可靠的程序。 ### 回答3: 目要求我们编写一个,实现从1到n共n个累加。 首先,我们需要明确函的输入和输出。根据目的要求,函的输入应该是一个正整n,输出应该是从1到n的累加和。 其次,我们需要考虑如何实现从1到n的累加。这里我们可以使用循环来实现。具体来说,我们可以使用一个变量sum来记录累加的结果,然后从1到n依次遍历,每遍历一个就将其加到sum即可。最后返回sum即可。 下面是一个具体实现: ``` function sum(n) { let sum = 0; for(let i = 1; i <= n; i++) { sum += i; } return sum; } ``` 以上代码,我们首先定义了一个变量sum,并初始化为0。然后使用for循环从1到n遍历,每次将遍历到的加到sum。最后返回sum即可。 测试一下: ``` console.log(sum(10)); // 输出55 ``` 经过测试,我们可以发现输出结果为55,说明我们的函实现是正确的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值