当前课程知识点:基于Linux的C++ >  第五讲 程序组织与开发方法 >  5.3 随机数库(一) >  LinuxCPP0503

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP0503在线视频

LinuxCPP0503

下一节:LinuxCPP0504

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP0503课程教案、知识点、字幕

现在就来考虑

怎么设计一个自己的随机数库

这里面就涉及到几个主题

第一个怎么来生成随机数

第二个库的基本的设计原则

应该遵照什么样的方案

来进行库的设计

第三个设计随机数库的接口

第四个 给出它的编码

就是随机数库的实现

最后一个步骤要对随机数库进行测试

整个库的设计就是按照基本的原则

定义好它的接口 给出它的实现

最后完成测试

首先来看随机数的生成

要求同学们写一个程序调用rand函数

来生成五个随机数

可以看到代码是这个样子

输出程序的提示信息之后

写了一个for循环

调用rand函数来生成一系列的随机数

rand函数特别的简单

一个参数都没有

生成的随机整数到底是哪一个呢

我们是不知道的

但是确确实实地知道

生成的这个随机数在什么样的范围里

就在0、RAND_MAX 在这个区间里

包括0和RAND_MAX

RAND_MAX这个值到底有多大

不同的操作系统

对这个值的定义是不一样的

同学们要根据你的操作系统来去看

RAND_MAX定义的最大的随机整数

到底是多少

rand调用一次

就在0、RAND_MAX的闭区间里边

生成随机整数

像这个例子一运行 生成五个随机数

看上去好像它们之间没有什么关系

再运行一遍

看出什么来没有 只要运行两次

一下就能够发现一个最重要的地方

就是虽说生成的这五个随机数

看上去就像一点关系都没有的样子

但是几次生成的随机序列本身

是一模一样的

也就是说 这个序列压根就不随机

问题是出在什么地方呢

就是操作系统

替你生成这个随机数的时候

它不是真的随机数

这是标准库替我们提供的

一个随机数发生器

它哪能真的随机呢

它的基本工作原理

就是使用一个特殊的值

作为随机数发生器的初始值

通过一系列的复杂的运算生成下一个数

然后使用下一个数作为新的初始值

再生成下下一个数

它是按照这样的方式

不断地去滚动地生成下一个随机数的

因为它内部计算做得很巧妙

看上去就像这些数之间

是完全无关的一样

但事实上只要你给了它一个

固定的初始值

那么你生成的那个随机序列

就一定是一样的

那怎么办呢

处理这个问题就需要

做一次额外的调用 就是srand

srand函数需要接受一个参数

返回值呢 没有

前面那个“s”是种子的意思

就是说调用一次srand

将会设定随机数发生器的种子

决定随机数发生器初始值是几

如果程序运行的时候

每次初始的x0都不一样

那么随机数发生器就会根据不同的x0

来替你生成随机数序列

那么怎么样才能让这个srand函数

在运行的时候

两次传递进去的参数是不一样的呢

找到两次运行的时候

它们之间的差异在什么地方

很容易就发现两次运行的时候

时间是不一样的

我们的时间是单向流逝的

那么在这种情况下边

我用当前时间 直接转换成整数

作为srand这个函数的种子传进去

程序第一次执行

去取它这次执行的那个时间

作为种子传给srand

程序第二次再执行

它就取第二次执行的时间

作为种子传给srand

你看两次传的参数是不一样的吧

传的是不同的x0

那两次运行得到的那个随机数

一定是不一样的

第一个随机数都不一样了

那后边的随机数肯定就都不一样了

整个序列就全变了

怎么得到当前的时间呢

在Linux操作系统下边

C标准库里提供一个函数

这个函数叫time

使用time来获得CPU的时间

time带一个参数

这个参数不用管 现在传0

事实上 那个参数不是要传进去的参数

其实讲完指针同学们就了解了

它是一个指向结构体的指针

它就是我带回来的那个时间

分成年月日 它是给你分解开的

time函数的返回值 事实上是一个整数

time_t的格式

就把它直接转换成整数 传给srand

但是同学们在调用srand函数的时候

需要特别注意

这个函数在你的程序运行的过程中

只能调用一次

生成一个随机数调用一次

那就不成了

因为像这样一个程序

它运行的时间是非常非常短的

生成了一个随机数

然后你再调用一次srand

再做一个种子 那时间都没变

每次都设置同样的种子

那生成的随机数

可不就是一模一样的吗

所以说特别记住这一条

srand只能调用一次

而且必须是在你生成

第一个随机数之前调用

此后再也不需要调用srand

就直接调用rand函数 生成随机数

接下来我们来看怎么设计随机数库

我把它总结出来

称之为接口设计的四项基本原则

一个用途一致

这个接口里边要提供一系列的函数

这些函数理论上来讲

应该是属于同一类问题的

用途不一致 别人就很难用

第二个操作要简单

在你给出设计方案的时候

要特别的注意这个库的操作

应该足够的简单 让别人很容易用

能够最大限度隐藏库的实现细节

第三个是功能充足

你设计了一个库

要提供足够的功能给别人用

如果功能本身就不足

那这个库的使用

实际上是非常非常受限的

第四个 性能稳定

库肯定要经过严格的测试

然后才能够知道

这样的一个库有没有存在

显而易见的缺陷

至少要保证库的实现

不存在显而易见的缺陷才行

这就是接口设计的四项基本原则

那么现在就来看

随机数库这个接口应该设计一些

什么样的函数

我们就想 定义这样的随机数库

提供什么样的功能呢 本来啊

C标准库里边提供rand函数

能够生成0、RAND_MAX

这样一个区间里的随机整数的随机数

可能不在0、RAND_MAX

这样一个区间里

比如说 在1、2、3、4、5、6

这6个数里边随便生成一个

模拟掷骰子 还比如讲

我想生成1到52之间的某一个数

模拟扑克牌

去掉大小王剩下就是52张牌

这个就叫随机数库

设计随机数库的时候要处理这个问题

就是说能够随机的生成

在指定的范围内的随机数

而不是固定的[0, RAND_MAX]

第二个我还要能够生成

指定范围内的随机的小数

尤其是0、1之间的随机的小数

它可以用来描述很多问题

比如说涉及到概率的

都可以用它来进行描述

这就是两个最主要的功能

随机数库就应该完成

能够生成指定范围内的随机整数

和指定范围内的随机实数

还有一个 需要对它进行随机化

就是设定随机数发生器的种子

有了这个概念之后

就可以定义随机数库的接口

这里面就提供了三个函数的原型

一个随机化Randomize

一个生成一个指定范围内的随机整数

一个生成指定范围内的随机实数

就三条函数原型

第一个没参数 没返回值

第二个双参数 双整数参数 返回整数

第三个双浮点数参数 返回浮点数

就这个格式 非常非常简单

接下来看怎么实现它

这是第一个 要随机化 调用srand

用time取当前的时间

把返回值转换成整数然后传给srand

就用它做种子

time这个函数这里面传的是NULL

这是预定义的宏

意思就是0

这就是随机数库srand这个函数的调用

用它来实现Randomize

接下来是两个函数的实现

生成一个随机的整数

和一个随机的实数

在实现这两个函数的时候

同学们需要特别注意

要保证这两个数能够平均地映射到

这个新的区间里面去

原来生成的数是0、RAND_MAX之间的

现在要把生成的随机数映射到

指定的low、high这样的区间里

这个区间的数可能 比较少 比如1到6

保证那些数据能够平均地映射成

1到6这六个数才成

如果是一个整数的话

那么如果不做特殊的运算

它是没有办法保证平均映射的

这里面还需要注意的地方

就是调用了exit函数退出

看到传的退出码

退出exit(1) 这个是exit(2)

传不同的退出码是可以的

接下来要对随机数库进行测试

在这里面的测试

不仅仅要测试随机数库里面

提供的每一个函数

单独要进行测试 还要联合测试

什么意思 就是说这个随机数库里面

提供三个函数嘛

每一个函数都需要测试

这样保证每一个函数

单独运行的时候没有问题的

第二个要保证这些函数

混合在一块运行的时候也没有问题

比如说随机化一次调用

然后随机生成几个整数

紧跟着又随机生成几个浮点数

再生成几个整数

然后我来看它的情况对不对

同学们在使用库的时候

实际上需要注意

先生成一个随机整数

紧跟着生成一个随机实数

又生成一个随机整数

又生成一个随机实数

交替着生成这些随机数有没有关系呢

原始生成的随机数就是0、RAND_MAX之间的

随机整数

然后映射成实数的 映射成小整数的

生成随机整数 随机实数

随机整数 随机实数 没关系

一定要记得 在生成随机数之前

你一定要调用Randomize进行随机化

库的使用者就需要知道这一条

对于库的设计者来讲方便了

三个函数写完就行了

对库的使用者来讲这就不太方便

在生成第一个随机实数

或者随机整数之前

一定要调用Randomize

如果忘了 就用错了

库的设计者怎么处理这个问题

我把这个问题留到课后

怎么重新设计随机数库

让用户不需要调用Randomize

就能够正确的生成随机序列呢

同学们课后好好地思考一下

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP0503笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。