百日算法 - 随机数的玩法

1,651 阅读5分钟

百日算法 - 随机数的玩法

在计算机中,由于表示小数的变量是有范围的,所以小数是有限的,并不像数学中小数可以无限的抽象循序下去,而且计算机中,随机数出现的概率是相同的。

处于对 C++ 的青睐,所以后续的算法更多都是通过 C++语言进行编写的。

可以使用cstdlib头文件中的rand()函数来获得随机整数;

这个函数返回0~RAND_MAX之间的随机整数;

rand()函数生成的是伪随机数。即每次在同一个系统上执行这个函数的时候,rand()函数生成同一序列的数。

rand()函数的算法使用一个叫种子的值来控制生成数字。默认情况下,种子的值是1,。如果改变种子为不同值,随机数也会不同。(只要随机数种子不变,其生成的随机数序列就不会改变。)

可以使用cstdlib头文件中的srand(seed)函数来改变种子的值。为确保程序中每一次种子的值不同,可以使用time(0)作为种子,即srand(time(0))

对于 C++ 的随机数,在 《C++程序设计》一书中 有以上的解释

好清楚的基本的语法特性,我们就开始验证第一个理论。

在计算机中的随机数的出现的概率都是相同的

先定义一个获取随机数的方法,该方法中未进行种子的初始化

#include <iostream>
#include "RandomNumber.h"using namespace std;
​
/**
 * 获取范围是 0~1 随机数
 * rand()  方法获取的随机数是 0 ~ 7FFF
 * @return
 */
double RandomNumber::getMyRumber() {
    return (double )rand()/RAND_MAX;
}

获取统计的数据结果

/**
* @author peggy
* @data 2023/2/15 15:32
*///
// Created by peggy on 2023/2/15.
//#include <ctime>
#include <cstdlib>
#include "countRandom.h"using namespace std;
​
double countRandom::getProbability(double limit) {
    RandomNumber randomNumber;
    //设置种子数
    srand(time(0));
    //统计符合提交出现的次数
    double counts = 0;
    for (int i = 0; i < RAND_MAX; ++i) {
        if (randomNumber.getMyRumber() < limit)
            counts++;
    }
    //出现的次数占总可能的大小
    return (double) (counts / RAND_MAX);
}

测试

#include <iostream>
#include "random/countRandom.h"
​
using namespace std;
​
int main() {
    countRandom Random;
    double limit = 0.5;
    double proBability = Random.getProbability(limit);
    cout << "小于 "<<limit<<" 出现的概率" << proBability << endl;
}

小于 0.3 出现的概率0.306284

小于 0.5 出现的概率 0.501358

小于 0.9 出现的概率0.899564

小于 1 出现的概率0.999939

所以通过上面的分析可以得出,每个随机数的分布的概率都是等比例的

从上面也可以看出,其实哈,我们的随机数出现的概率是一条 y=x 直线,那么如果我们要讲概率改变为 y=x^2 变为一个曲线,那么我们该怎么处理呢?

首先分析一下 ,由于我们知道,产生的随机数,出现的概率都是相同的,我们要让出现的概率翻倍。

也就是说假设,我们要从篮子里取出一堆有编号的球(编号从1~10),而这些球的编号都是唯一的,那么取出的球的概率也是相同的。现在有两个相同规格的篮子与球,我们先划定一个界限为小于等于 8,那么随机拿出一个球,在同一个篮子中的概率就是 4/5。

现在规则改变,分别从1 号篮子与 2 号篮子摸一个球,并且要球是大于 5 的那么,1号篮子的概率是 1/2 ,2 号篮子的概率也是 1/2 。那么取这两个球中的最大一个球的编号,然后与 5 进行比较,此时获取到的编号大于 5 的概率是多少呢?

这个相当于你抽奖,抽了两次,但是你两次对比的界限是没有改变的。

所以两次抽奖或者两次摸球的事件是完全独立的,但是你中奖的几率却由原来的概率进行了翻倍,对于上面的案例来说,你摸了两次,那么摸中大于 5 的概率,就有原先独立事件的 1/2 改变,变为了 (1/2)^2 = 1/4 ,相比如摸中大于 8 的概率 (1/5)^2=1/25。

所以根据上述的结论,编写代码即可得到符合 F(x)=x^2 的随机数

#include <iostream>
#include "random/countRandom.h"
#include "random/RandomNumberToFunction.h"
​
using namespace std;
​
int main() {
    countRandom Random;
    double limit = 0.9;
    
    double proBability2 = Random.getProbabilityTo2(limit);
    cout << "小于 " << limit << " 出现的概率 " << proBability2 << endl;
    cout << "计算的概率 " << limit * limit << endl;
}
/**
* @author peggy
* @data 2023/2/15 15:32
*///
// Created by peggy on 2023/2/15.
//#include <ctime>
#include <cstdlib>
#include "countRandom.h"
#include "RandomNumberToFunction.h"using namespace std;
struct RandomNumberToFunction randomNumberToFunction;
​
double countRandom::getProbabilityTo2(double limit) {
    double counts = 0;
    for (int i = 0; i < RAND_MAX; ++i) {
        double num = randomNumberToFunction.getMAXRandom();
        if (num < limit)
            counts++;
    }
    return (double) (counts / RAND_MAX);
}
/**
* @author peggy
* @data 2023/2/15 20:23
*///
// Created by peggy on 2023/2/15.
//#include "RandomNumberToFunction.h"
#include "DataUtils.h"/**
 * 将获取的随机数转化为 y=x^2
 * @return
 */
struct RandomNumber randomNumber;
struct DataUtils dataUtils;
​
double RandomNumberToFunction::toRandomFunction() {
    return this->getMAXRandom();
}
​
double RandomNumberToFunction::getMAXRandom() {
    return dataUtils.getMAX(randomNumber.getMyRumber(),randomNumber.getMyRumber());
}
/**
* @author peggy
* @data 2023/2/15 15:13
*///
// Created by peggy on 2023/2/15.
//#include <iostream>
#include "RandomNumber.h"using namespace std;
​
/**
 * 获取范围是 0~1 随机数
 * rand()  方法获取的随机数是 0 ~ 7FFF
 * @return
 */
double RandomNumber::getMyRumber() {
    return (double )rand()/RAND_MAX;
}

最终运行结果

大于 0.9 出现的概率 0.808496 计算的概率 0.81

Process finished with exit code 0

当然通过上面的的案例,所以很容易的可以得出,如果要让其概率变为原来的 X^3 , 那么只需要进行三次的 getMAX(randomNumber.getMyRumber(),getMAX(randomNumber.getMyRumber(),randomNumber.getMyRumber()));即可,这里就不在赘述演示了。

那么有没有好奇过,这里如果不是 getMAX(),获取的最大值而是最小值 getMIN() 呢?那么获取的概率代表的什么呢?

其实这个也是比较好推导的,首先 getMIN() 获取的一定是最小值,那么对于 ( 0 ,X ] 而言,如果 getMIN()获取的值比 X 还要大,那么肯定是可以保证 ,另一个随机数也是比 X 还要大的,所以就是数必定是落在了 [ X , 1 ),那么这个获取的最终概率就是 [ X , 1 ) 的概率也就是 (1-X)^2,那么对于 ( 0,X ] 的概率就变成了 1-(1-X)^2

为了验证这个结论猜想,我们只需修改两点,代码修改如下。

#include <iostream>
#include "random/countRandom.h"
#include "random/RandomNumberToFunction.h"
​
using namespace std;
​
int main() {
    countRandom Random;
    double limit = 0.3;
​
    double proBability2 = Random.getProbabilityTo2(limit);
    cout << "小于 " << limit << " 出现的概率 " << proBability2 << endl;
    cout << "计算的概率 " << 1 - (1 - limit) * (1 - limit) << endl;
}
/**
* @author peggy
* @data 2023/2/15 20:23
*/

//
// Created by peggy on 2023/2/15.
//

#include "RandomNumberToFunction.h"
#include "DataUtils.h"

/**
 * 将获取的随机数转化为 y=x^2
 * @return
 */
struct RandomNumber randomNumber;
struct DataUtils dataUtils;

double RandomNumberToFunction::toRandomFunction() {
    return this->getMAXRandom();
}

double RandomNumberToFunction::getMAXRandom() {
    return dataUtils.getMIX(randomNumber.getMyRumber(),randomNumber.getMyRumber());
}
/**
* @author peggy
* @data 2023/2/15 20:19
*/

//
// Created by peggy on 2023/2/15.
//

#include "DataUtils.h"

double DataUtils::getMAX(double num1, double num2) {
    return num1 > num2 ? num1 : num2;
}

double DataUtils::getMIX(double num1, double num2) {
    return num1 < num2 ? num1 : num2;
}

输出结果:

小于 0.3 出现的概率 0.505234 计算的概率 0.51

Process finished with exit code 0

小于 0.8 出现的概率 0.959685 计算的概率 0.96

Process finished with exit code 0

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天