c++随机数问题研究

news2024/11/24 9:03:34

1、问题背景

某项目中有个复杂的排序,先是各种规则依次排序,最后如果依然并列的话,那就随机位置,名次并列。测试中发现一个诡异现象,并列时随机排序但随机后2个case打印的顺序每次都一样,随机数没有起到任何作用。经过分析发现,随机数种子srand(clock()),本意是希望连续调用这个函数,给多个随机数设置种子,实际上设置的种子相同,最后产生的随机数是伪随机数。那么有没有一种随机数方法可以在较快的循环中,保证随机性呢?

原问题较复杂,给个类似的例子说明具体场景:

void test_random()
{
    vector<int> vec;
    vec.resize(100);
    iota(vec.begin(), vec.end(), 1);
    vector<int> vec2(vec);
    
    srand(clock());
    random_shuffle(vec.begin(), vec.end());
    srand(clock());
    random_shuffle(vec2.begin(), vec2.end());
​
    int cnt = 0;
    cout << "vec:" << endl;
    for (auto v : vec) { 
        cout << v << "  "; 
        if ((++cnt % 10) == 0) cout << endl;
    }
    cout << endl<< endl;
​
    cnt = 0;
    cout << "vec2:" << endl;
    for (auto v : vec2) {
        cout << v << "  ";
        if ((++cnt % 10) == 0) cout << endl;
    }
}

输出结果为:

2、rand()和srand()

rand()和srand()是c函数,在stdlib.h中定义,rand()能产生0--32767范围的随机数。

如果只使用rand,则每次输出的随机数都是一样的,相当于使用srand(1)作为默认种了。如果给定种了,则能产生不同的随机数,所以time或clock函数就是一个好种子,获取计算机的时间,用秒或毫秒来做随机数种子以产生不同的随机数。但是在某些场景下,会引发下列问题:

问题1:在程序运行较慢或不需要连续产生随机数时,用时钟当做种子没有问题,但要快速产生不同的组数的随机数时,就会出现前面出现的现象,较大概率出现相同的随机数。

问题2:如果希望生成某个范围的随机数,则不好控制,通常会采用取模的方式,而这种方式会破坏随机数的分布概率。

// 0--10 的随机数
srand((unsigned int)time(NULL));
int r = rand() % 10
​
// 100--200的随机数
int min = 100;
int max = 200;
srand((unsigned int)time(NULL));
int r = rand() % (max - min) + min
    
// [0--1.0] 浮点数
srand((unsigned int)time(NULL));
float r = rand() % RAND_MAX 

3、c++11 随机数

c++11引入了random头文件,可以更加精确的产生随机数,并且提供了完善的操作接口。C++标准规定了随机数设施,包括均匀随机位生成器(Uniform random bit generators,URBG)和随机数分布等,定义在<random>中。

参考文档:http://www.cplusplus.com/reference/random/?kw=random

This library allows to produce random numbers using combinations of generators and distributions:

  • Generators: Objects that generate uniformly distributed numbers.

  • Distributions: Objects that transform sequences of numbers generated by a generator into sequences of numbers that follow a specific random variable distribution, such as uniform, Normal or Binomial.

random标准款主要包括:

生成器:生成均匀分布伪随机数的对象

分布:将生成器生成的数序列转换为某种特定数学概率分布的序列,如均匀分布、正态分布、泊松分布等。

3.1、生成器

1)random_device生成器

C++11提供了一个random_device随机数类,英文叫“Non-deterministic random number generator”,这是一个非确定性随机数生成器,它并不是由某一个数学算法得到的随机序列,而是通过读取文件,读什么文件看具体的实现(Linux可以通过读取/dev/random文件来获取)。文件的内容是随机的,简单理解即这个类依靠系统的噪声产生随机数。

2)伪随机数引擎

伪随机数引擎,实现方式属于模板类,是使用算法根据初始种子生成伪随机数的生成器。

linear_congruential_engine:线性同余生成引擎,是最常用也是速度最快的,但随机效果一般
mersenne_twister_engine:梅森旋转算法,随机效果最好。
subtract_with_carry_engine:滞后Fibonacci算法。

随机数引擎需要一个 整型参数作为种子,对于给定的单个或多个种子,随机数生成器总会生成相同的序列,这在测试时非常有用。当测试完成,则需要随机的种子以产出不同的随机数,推荐使用random_device作为随机数种子。

3.2、适配器

除了生成器模板库外,c++11还设计了几种适配器。

discard_block_engine: Discard-block random number engine adaptor (class template ) independent_bits_engine: Independent-bits random number engine adaptor (class template ) shuffle_order_engine :Shuffle-order random number engine adaptor (class template )

3.3、随机分布模板类

随机数引擎产生的随机数值都比较大,使用时经常需要限定到一个范围内,c++11提供了符合各种概率分布的随机数生成模板类,比如:均匀分布,正态分布,泊松分布等。

以均匀分布为例:

template< class IntType = int > class uniform_int_distribution;
template< class RealType = double > class uniform_real_distribution;

测试1:直接使用引擎产生随机数,范围很大。

random_device rd;
mt19937 g(rd());
for (int n = 0; n < 10; ++n)
{
    cout << g() << " ";
}
/* 输出
case 1: 
649838310 2697128147 116396177 1728659882 2608399735 1196122003 1824385544 3670102805 2610106284 1577110367
case 2:
2220490604 2877041131 4118289859 1423499548 3901014967 230558428 3106974485 2887363336 1389836600 4020707730
*/

测试2:使用均匀分布类模板产生随机数,可以限定生成的随机数的范围。

random_device rd;
mt19937 g(rd());
uniform_int_distribution<> dis(1, 100);
for (int n = 0; n < 10; ++n)
{
    cout << dis(g) << " ";
}
/* 输出
case 1:  67 23 61 3 91 88 81 61 57 60
case 2:  51 1 29 75 81 32  8  8 47 5
cae  3:  92 1 22 24 84 20 72 27 66 39
*/

3.4、用法总结

1、定义种子,可以是随机种子或者固定种子,固定种子方便测试用,但每次产生的随机数都一致。

2、选择随机引擎,把种子值传入当做参数。

3、选择合适分布方式,创建随机分布对象,可以在此时指定需要的随机数的范围。

4、把引擎传入随机数分布模板类对象,输出随机数。

4、问题解决

c++ 提供了一个shuffle函数,相比于random_shuffle,shuffle可以指定随机数引擎,如果指定一个非确定性引擎,则能保证连续生成的两组随机数各不相同,达到设计效果。

template <class _RanIt, class _Urng>
void shuffle(_RanIt _First, _RanIt _Last, _Urng&& _Func)

修改后的测试函数:

void test_random()
{
    vector<int> vec;
    vec.resize(100);
    iota(vec.begin(), vec.end(), 1);
    vector<int> vec2(vec);
    
    auto engine = std::default_random_engine(std::random_device()());
    shuffle(vec.begin(), vec.end(), engine);
    shuffle(vec2.begin(), vec2.end(), engine);
     
​
    int cnt = 0;
    cout << "vec:" << endl;
    for (auto v : vec) { 
        cout << v << "  "; 
        if ((++cnt % 10) == 0) cout << endl;
    }
    cout << endl<< endl;
​
    cnt = 0;
    cout << "vec2:" << endl;
    for (auto v : vec2) {
        cout << v << "  ";
        if ((++cnt % 10) == 0) cout << endl;
    }
}

上面例子using default_random_engine = mt19937; 其中,mt19937是一个引擎,最大值为0Xffffffff。

using mt19937 = mersenne_twister_engine<unsigned int, 32, 624, 397, 31, 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15,  0xefc60000, 18, 1812433253>;

输出结果为:

vec:
85  7  58  8  29  17  60  57  81  71
82  93  4  47  84  40  65  79  37  24
3  14  36  25  32  16  91  48  86  38
63  78  80  28  44  39  34  90  69  13
74  1  77  59  88  41  46  56  33  62
21  18  30  52  89  22  87  27  9  53
70  51  2  72  92  42  26  66  73  97
15  43  31  49  100  68  54  35  12  99
6  67  5  96  94  83  10  45  61  50
23  76  19  98  11  55  75  20  95  64

vec2:
37  51  12  62  99  95  65  1  78  29
80  13  48  72  83  23  25  75  97  68
86  40  24  30  84  4  47  28  76  57
33  38  16  18  69  9  70  31  42  49
52  71  91  96  81  73  34  45  10  26
2  93  89  41  54  64  44  22  36  39
87  43  63  55  3  32  27  19  85  79
35  5  58  11  56  59  21  88  15  100
74  53  8  14  60  92  17  50  7  90
6  20  67  77  98  61  66  82  46  94

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51089.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于PHP+MySQL个人信息管理系统的设计与实现

随着现代工作的日趋繁忙,人们越来越意识到信息管理的重要性与必要性,而具有个性化特色的个人信息管理系统能够高速有效的管理个人信息,从而提升自己的工作效率。该系统的基本功能包括用户登录,个人信息管理,通信名录管理,日程备忘管理,财物管理,随手笔记管理,修改密码等功能。 …

html+css+javascript+jquery+bootstarp响应式旅行社旅游平台网站模板(14页)

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

[附源码]Python计算机毕业设计SSM考试排考系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

乾元通多卡聚合设备 消防行业应用解决方案

近年来,灭火救援工作呈现出突发性强、处置难度大、作战时间长等特点&#xff0c;如何获取灾害现场的第一手资料为救援行动提供决策依据&#xff0c;如何在处置灾害事故的过程中保证多个消防队伍协同配合、指令畅通&#xff0c;对室外无线环境下的视/音频即时通信&#xff0c;提…

深度学习——池化层笔记+代码

1.对于卷积层来说&#xff0c;卷积对位置比降敏感 ①检测垂直边缘 使用K[-1,1] 的卷积核进行卷积操作&#xff0c;实现垂直边缘信息的检测 如果因为抖动曝光啥的同一个物体&#xff0c;1像素可能往右移位了。K的卷积核就得不到相应位置的边缘信息。 ②需要一定程度的平移不变性…

代码随想录训练营第36天|LeetCode 435. 无重叠区间、763.划分字母区间、 56. 合并区间

参考 代码随想录 题目一&#xff1a;LeetCode 435.无重叠区间 怎么判断重叠 按照题目给出的示例&#xff0c;第一个区间的右边界与第二个区间的左边界重合不算重叠。对于区间问题&#xff0c;一般都要对区间进行排序&#xff0c;可以按照左边界或者右边界排序。按照个人习惯…

[附源码]Python计算机毕业设计Django二手书店设计论文

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

8. 抓到Netty一个隐藏很深的内存泄露Bug | 详解Recycler对象池的精妙设计与实现

抓到Netty一个隐藏很深的内存泄露Bug | 详解Recycler对象池的精妙设计与实现 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的时候&#xff0c;不小心用我的肉眼抓到了一个隐藏很深很深的内存泄露 Bug。 于是笔者将这个故事....哦不 .....事故&#…

介绍一款特别好用的java反编译工具jd-gui

目录 写在前面 开始 写在前面 之前用过另一款java反编译工具jad 但是这个工具有个问题就是对于一些java8的新特性&#xff0c;比如lambda表达式是解析不出来的&#xff0c;更不用说java9和java17了。关于这款工具的使用方法就不再这里赘述了&#xff0c;如果你感兴趣可以在网…

【JVM】主要组成部分及其作用

大致结构 JVM包含两个子系统和两个组件&#xff0c;两个子系统为Class loader(类装载)、 Execution engine(执行引擎)&#xff1b;两个组件为Runtime data area(运行时数据 区)、Native Interface(本地接口)。 Class loader(类装载)&#xff1a;根据给定的全限定名类名(如&…

跳槽有技巧?超强测试开发面试经验等你pick

每一次跳槽&#xff0c;相信大家都有自己的一套方法论&#xff0c;一套准备和选择过程。当然面试也是一个双向选择&#xff0c;不排除有的时候看“缘分”。不管怎么说&#xff0c;这几年来&#xff0c;在面试过大大小小挺多家公司后&#xff0c;也算职场老鸟一枚了&#xff0c;…

论文浅尝 | Hybrid Transformer Fusion for Multimodal KG Completion

笔记整理&#xff1a;陈子强&#xff0c;天津大学硕士&#xff0c;研究方向为自然语言处理论文链接&#xff1a;https://arxiv.org/pdf/2205.02357.pdf动机尽管多模态知识图谱补全较单模态知识图谱补全已经有了很大的改进&#xff0c;但仍然存在两个限制。&#xff08;1&#x…

Java内存模型与线程(1)

文章目录1. 概述2. 硬件的效率与一致性3. Java内存模型3.1 主内存与工作内存3.2 内存间交互操作并发处理的广泛应用是使得 Amdahl定律代替摩尔定律成为计算机性能发展源动力的根本原因&#xff0c;也是人类压榨计算机运算能力最有力的武器。Amdahl定律通过系统中 并行化与串行化…

MyBatis ---- MyBatis的高级查询功能

MyBatis ---- MyBatis的高级查询功能1. 查询一个实体类对象2. 查询一个list集合3. 查询单个数据4. 查询一条数据为map集合5. 查询多条数据为map集合方式一方式二1. 查询一个实体类对象 User getUserById(Param("id") int id);<!--User getUserById(Param("id…

Linux 内存虚实内存映射

Linux 内存虚实内存映射TOC 以前关于虚拟地址和物理地址的学习只是在书本上&#xff0c;今天在实际的开发板上实践了一下 代码&#xff1a; #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/sched.h #i…

【Pandas数据处理100例】(九十一):Pandas读取txt文本文件

前言 大家好,我是阿光。 本专栏整理了《Pandas数据分析处理》,内包含了各种常见的数据处理,以及Pandas内置函数的使用方法,帮助我们快速便捷的处理表格数据。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPandas版本:1.3.5N…

grid管理下的多实例配置不同监听端口

某现场有两个实例&#xff0c;且客户要求两个实例使用不同的端口&#xff0c;但是grid管理下的监听默认只能一个端口&#xff0c;可以通过以下方式解决 oracle下&#xff1a; srvctl add listener -l listener_cc -p 1522 -o $ORACLE_HOME srvctl add listener -l listener_…

[附源码]SSM计算机毕业设计学院竞赛管理信息系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

还不会使用 Vue-Router?请看过来

目录注明&#xff1a;路由的概念什么是路由和前端路由路由的分类配置Vue-router安装实例化router挂载配置路由一个路由匹配 多组件视图使用路由声明式路由导航 router-link编程式路由导航缓存路由组件 &#xff08;keep-alive&#xff09;注明&#xff1a; Vue-router 3.x的版本…

如何保持电机安全运行

介绍 电动机在电子系统中的使用已变得普遍。电机尺寸、控制和成本效率方面的创新使设计人员能够将电机添加到系 统中&#xff0c;从而创造新功能并扩展最终产品的功能。 随着电动机数量的急剧增加&#xff0c;对功率效率的担忧促使系统设计人员使用更高电压的电机并提高其设计…