事情的开始是这样的,在大二的时候,写了几种排序算法,为了测试,就要为数组(或者容器)赋予一些随机初值,自然就用到了C/C++中的随机函数。

  当时为了调用简单,将随机数赋值的过程写到了一个单独的函数里,这样一来,为数组(或容器)赋值就可以简洁高效。

  但是,问题就是,按理来说,每次调用都该得到不同的随机数列,然而事实证明,“接连被赋值的数组”得到的随机数列却是一样的...

  简述一下当时发现问题的地方:调用srand函数生成随机数序列,以当前计算机时间作为随机数种子,即srand((unsinged)time(NULL)),这个随机数初始容器函数是这样写的:

// 来自大二时候的Modnar,嗯,码写得很可爱...
void Random(int list[], int n) {
srand((unsigned)time(NULL));
for(int i = ; i < n; i++) {
list[i] = rand() % + ;
}
}

  结果当一个函数(比如main中)调用这个初始化函数时,就会发生问题,比如这样调用:

int main(int argc, char *argv[]) {
int lst1[], lst2[];
Random(lst1, );
Random(lst2, );
// Process...
return ;
}

  此时得到的lst1和lst2就会是相同的两个序列。

  这里简单解释一下,因为time这个函数,传入的参数是一个指向需要被修改的对象的指针,返回值是计算机自起始时间起经过的时间(单位为秒),所以... 由于这个调用过程,时间用不到1s,自然两次调用时传入srand的数值是一样的,因此其生成的随机数序列就是一样的...

  当然,这是现在了解了其中的原理后作出的解释,当时可爱的Modnar自然也有自己的想法,他是这样解释的:

  嗯,为年轻而又热爱思考的Modnar点赞。当时的Modnar是利用这段码来验证想法的:

 #include <time.h>
#include <stdlib.h> #include <iostream>
#include <fstream> #define N 20 using namespace std; ofstream out; int list[N]; //最开始定义的random函数。
void Random1(int list[], int n) {
srand((unsigned)time(NULL));
for(int i = ; i < n; i++) {
list[i] = rand() % + ;
}
} //代码同Random1()一样,而后发现其输出的序列同Random1()相同。
void Random2(int list[], int n) {
srand((unsigned)time(NULL));
for(int i = ; i < n; i++) {
list[i] = rand() % + ;
}
} //最初试图通过先进行for空循环延时,来实现time返回值不同,后发现行不通(表明srand()功能实现的原理)。
void Random3(int list[], int n) {
srand((unsigned)time(NULL));
int k = rand() % + ;
for(int i = ; i < k; i++) { }
for(int i = ; i < n; i++) {
list[i] = rand() % + ;
}
} //通过前三个“随机”函数,发现了可以实现目标功能的随机函数。
void RandomFinal(int list[], int n) {
static int k = ; //通过静态变量来找到上次调用rand()后,rand()的位置
srand((unsigned)time(NULL)); //随机数种子...
for(int i = ; i < k * n; i++) {
rand(); //每次执行本函数,就先“跳过” k * n 个rand()
}
k++;
for(int i = ; i < n; i++) {
list[i] = rand() % + ; //将跳过 k * n 个rand()后的随机序列赋值给list
}
} void showlist(int list[], int n) {
for(int i = ; i < n; i++) {
out << list[i] << " ";
}
out << endl;
} int main() {
out.open("output.txt");
if(!out) {
cout << "Error. Exit the program." << endl;
exit();
} else cout << "Open Succeessfully." << endl;
int locallist[N];
out << "Local List:" << endl;
out << "First time:" << endl;
out << "Random1:" << endl;
Random1(locallist, N);
showlist(locallist, N);
out << "Random2:" << endl;
Random2(locallist, N);
showlist(locallist, N);
out << "Random3:" << endl;
Random3(locallist, N);
showlist(locallist, N); out << "Second time:" << endl;
out << "Random1:" << endl;
Random1(locallist, N);
showlist(locallist, N);
out << "Random2:" << endl;
Random2(locallist, N);
showlist(locallist, N);
out << "Random3:" << endl;
Random3(locallist, N);
showlist(locallist, N);
out << endl; out << "Global List:" << endl;
out << "First time:" << endl;
out << "Random1:" << endl;
Random1(list, N);
showlist(list, N);
out << "Random2:" << endl;
Random2(list, N);
showlist(list, N);
out << "Random3:" << endl;
Random3(list, N);
showlist(list, N); out << "Second time:" << endl;
out << "Random1:" << endl;
Random1(list, N);
showlist(list, N);
out << "Random2:" << endl;
Random2(list, N);
showlist(list, N);
out << "Random3:" << endl;
Random3(list, N);
showlist(list, N); out << endl << "FinalRandom:" << endl;
out << "Local List:" << endl;
out << "First time" << endl;
RandomFinal(locallist, N);
showlist(locallist, N);
out << "Second time:" << endl;
RandomFinal(locallist, N);
showlist(locallist, N);
out << "Third time:" << endl;
RandomFinal(locallist, N);
showlist(locallist, N); out << endl << "Global List:" << endl;
out << "First time:" << endl;
RandomFinal(list, N);
showlist(list, N);
out << "Second time:" << endl;
RandomFinal(list, N);
showlist(list, N);
out << "Third time:" << endl;
RandomFinal(list, N);
showlist(list, N); out.close();
return ;
}

Random.cpp

  其实我很懂Modnar的想法(23333,手动狗头),也就是说,其核心思想,就是srand会根据一个已有的巨大随机数序列来确定具体抽取哪段来作为函数返回(即所需的随机数序列)。

  而且,当时的Modnar给出的解决方法是正确的(多么令人惊喜)!通过用static来“储存这时的随机数序列状态”。

  如今,在略有了解std::C++11后,可以继续讨论一下这个问题(由于涉及到了一点儿很简单的自己写的东西,这里就不贴代码了):   

  在C++中,可以通过随机数引擎(相当于srand)来生成这个随机数序列,通过随机数分布(即图中的distribution)来进一步映射。

  需要注意的是distribution的参数是随机数引擎,因为一些分布可能不只需要一个数值...(先这么理解吧,这儿不是主角)

  下面是程序输出:

  重点就是,可以看到,通过定义成static类型的引擎、分布对象,可以使得生成的随机序列不同!

  (这里,不禁感叹当时Modnar的奇思妙想... 尽管这些东西翻翻书就能学到,哈哈哈哈)

  这部分,在《C++ Primer 5th》有所提及:“一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其(包括引擎和分布对象)定义为static的。否则,每次调用函数都会生成相同的序列。”(原书中文版第662页,英文版第748页)

  书中强调的是直接使用引擎对象的情况下(即不给引擎初始时传入一个随机数种子),当然,上面已经分析过,尽管传入一个随机数种子(比如(unsigned)time(nullptr)这个种子值),往往会因为调用时间间隔太短而无法生效。

  顺便说一下,在C++11中,对随机数的支持也趋渐完善,通过简单地调用随机数引擎以及不同类型的分布(例如正态分布等),也可以实现很多实用的数据生成功能,还是很值得一玩的。

  当然,偶尔胡思乱想、任意猜猜也是挺有意思的,日后想起来,真的很深刻...(手动调皮)

  @编辑于2019.3.7

C++中的随机数的更多相关文章

  1. 为什么说Java中的随机数都是伪随机数?

    什么是伪随机数?  1.伪随机数是看似随机实质是固定的周期性序列,也就是有规则的随机. 2.只要这个随机数是由确定算法生成的,那就是伪随机,只能通过不断算法优化,使你的随机数更接近随机.   (随机这 ...

  2. Linux中的随机数文件 /dev/random /dev/urandom

    Linux中的随机数可以从两个特殊的文件中产生,一个是/dev/urandom.另外一个是/dev/random.他们产生随机数的原理是利用当前系统的熵池来计算出固定一定数量的随机比特,然后将这些比特 ...

  3. Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom

    Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom 文中的 Random即:java.util.Random,ThreadLocalRandom 即: ...

  4. numpy中的随机数模块

    https://www.cnblogs.com/td15980891505/p/6198036.html numpy.random模块中提供啦大量的随机数相关的函数. 1 numpy中产生随机数的方法 ...

  5. Java课程课后作业190315之从文档中读取随机数并得到最大连续子数组

    从我上一篇随笔中,我们可以得到最大连续子数组. 按照要求,我们需要从TXT文档中读取随机数,那在此之前,我们需要在程序中写入随机数 import java.io.File; import java.i ...

  6. Java中产生随机数的两个方法

    Java中产生随机数的两个方法 一.利用random方法来生成Java随机数. 在Java语言中生成Java随机数相对来说比较简单,因为有一个现成的方法可以使用.在Math类中,Java语言提供了一个 ...

  7. 关于LUA中的随机数问题

    也许很多人会奇怪为什么使用LUA的时候,第一个随机数总是固定,而且常常是最小的那个值,下面我就简要的说明一下吧,说得不好,还请谅解.我现在使用的4.0版本的LUA,看的代码是5.0的,呵呵 LUA4. ...

  8. C语言中生产随机数 rand()函数

    参考资料:C语言中产生随机数 一:如果你只要产生随机数而不需要设定范围的话,你只要用rand()就可以了:rand()会返回一随机数值, 范围在0至RAND_MAX 间.RAND_MAX定义在stdl ...

  9. LoadRunner中的随机数

    LoadRunner中的随机数 Action() { int i; ]; srand(time(NULL)); i=rand()%; lr_save_datetime("%m%d%H%M%S ...

  10. [Linux] 如何在 Linux 中提取随机数

    如何在 Linux 中提取随机数 一.设备文件 /dev/random & /dev/urandom 字符特殊文件 /dev/random 和 /dev/urandom (存在于Linux 1 ...

随机推荐

  1. 阿里云服务器-2.使用putty连接

    1.下载PuTTY 进入官网 点击我跳转 点击here 看自己电脑是多少位选择下载 2.安装 一直点击下一步就行 这里可以选择也可以不选择,这会在创建桌面快捷文件 看图注意事项 这里也可以用密匙,先去 ...

  2. word写文档体会

    1.找一个文档规范要求. 2.根据文档的规范要求调整正文的格式,标题1的格式,标题2的格式,标题3的格式,图表的格式,把没用的那些格式都删除掉. 3.图注表注后空格一行. 4.设置页眉页脚. 5.生成 ...

  3. js中的局部函数和全局函数的调用

    //局部函数和全局函数的特点 function fc1(){ var name ="chenhao"; function fc2(){ var age = 30; alert(na ...

  4. 使用session在jsp页面之间传递多维数组,用于实现全局变量的效果

    使用session在jsp页面之间传递多维数组:发送数据的jsp页面:int [][] form_number=new int[4][4]; session.setAttribute("se ...

  5. [JLOI2009]神秘的生物

    题目链接 题目大意 给定一个\(n*n\)的矩阵,从其中选取恰好一个连通块,使选取的格子所对应的权值和最大. \(n\leq 9\) 解题思路 由于\(n\)特别小,考虑插头dp. 和一般的插头dp不 ...

  6. 惠普台式机,如何选择U盘启动

    开机先连续点击键盘F9按键进入选择启动盘界面,找到自己的U盘(KingstonDataTraveler 3.0)

  7. No space left on device(转载)

    本文转自 http://blog.163.com/ly_89/blog/static/186902299201191233058625/ =====================概述======== ...

  8. JS中bool值转换与比较

    前言 首先需要知道的是,js中有6个值为false,分别是: 0, '', null, undefined, NaN 和 false, 其他(包括{}, [], Infinity)为true. 可以使 ...

  9. ndarray数据类型及转换

    ndarray数据类型 Ndarray的基本数据类型如下图所示,数据类型的命名采用“类型名+数字”的形式表示,数字表示数据的比特位长.在计算机中比特位bit是表示数据最小的单位,1个字节Byte的长度 ...

  10. PTA的Python练习题(十二)-第4章-7 统计学生平均成绩与及格人数

    第4章-7 统计学生平均成绩与及格人数 a=eval(input()) b=list(map(int,input().split())) sum=sum(b) c=[i for i in b if i ...