事情的开始是这样的,在大二的时候,写了几种排序算法,为了测试,就要为数组(或者容器)赋予一些随机初值,自然就用到了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. 【网搜】禁止 number 输入非数字(Android仍有问题)

    目的:使用 number 表单,让其只可输入数字. 问题:ios 可正常限制,Android 仍可输入  [ e | . |  - |  + ]   这4个字符.猜测这4个字符在数值中为科学记数.小数 ...

  2. Android SDK Tools,Platform-tools,Build-tools分别有什么作用?

    SDK Tools:是下载sdk最基础的,由它再来下载Platform-tools,Build-tools platform-tools包含开发app的平台依赖的开发和调试工具,包括 adb.fast ...

  3. Linux的一些命令(CentOS7)

    注:下面的文件名和文件夹名字都支持通配符 init 0:关机 init 6:重启 clear:清屏 ip addr:查看ip地址 passwd 用户名:修改指定用户的密码 时间相关的命令: date: ...

  4. Abaqus-GUI开发-RSG

    目录 1. GUI开发简介 2. 目标和消息 2.1消息类型和消息ID 2.2消息映射 3. 控件创建 1. GUI开发简介 Abaqus GUI程序开发时,可以采用两种方式创建GUI图形界面. (1 ...

  5. 使刚编辑的vim编辑器配置文件立即生效(实为自动生效)

    简单的说,在虚拟机下安装的Centos6.3系统后,默认的是没有vim编辑器的配置文件,此时如果有必要,可以按照自己的习惯定制或配置自己的vim编辑器: 1,如果你是root权限,进入root目录下: ...

  6. notepad++ java编码,输出中文字符时,编译出错

    呆在公司中,最近受开发手机app的几个同事影响,想学android的开发,心血来潮,挡也挡不住,说干就干,直接看教程,发现有很多关于java的语法知识不懂,于是又来学java,学习的过程中难免出现问题 ...

  7. C:数值溢出问题

    当超过一个数据类型能够存放最大的范围时,数值会溢出. 有符号位最高位溢出的区别:符号位溢出会导致数的正负发生改变,但最高位的溢出会导致最高位丢失. #include <stdio.h> i ...

  8. MediaCreationTool制作WIN10安装U盘,安装纯净版win10的通用教程

    注意: 1.准备8G或8G以上U盘. 2.安装系统前备份好个人需要数据(制作U盘会格式化U盘,U盘内有需要的数据也事先备份好) 3.有预装office的务必记住自己激活office账户和密码以免重装后 ...

  9. 吴裕雄--天生自然Numpy库学习笔记:NumPy 线性代数

    import numpy.matlib import numpy as np a = np.array([[1,2],[3,4]]) b = np.array([[11,12],[13,14]]) p ...

  10. Codeforces Round #618 (Div. 2)C. Anu Has a Function

    Anu has created her own function ff : f(x,y)=(x|y)−y where || denotes the bitwise OR operation. For ...