c++ 随机数问题的相关研究

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

c++随机数问题研究

原创首发:https://www.cnblogs.com/pingwen/p/14496607.html

以上就是c++ 随机数问题的相关研究的详细内容,更多关于c++随机数问题研究的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++常见获取随机数的方法小结

    本文实例讲述了C++常见获取随机数的方法.分享给大家供大家参考,具体如下: 方法一: 使用 rand 函数可以获取,如下. #include<iostream> #include<ctime> using namespace std; int main() { for (int i = 0; i < 10; i++) cout << rand() << endl; return 0; } 随机数大小是在0到RAND_MAX,值为2147483647,它

  • C++产生随机数的实现代码

    C++怎样产生随机数:这里要用到的是rand()函数, srand()函数,C++里没有自带的random(int number)函数. (1) 如果你只要产生随机数而不需要设定范围的话,你只要用rand()就可以了:rand()会返回一随机数值, 范围在0至RAND_MAX 间.RAND_MAX值至少为32767.例如: 复制代码 代码如下: #include<stdio.h>#include <iostream> 复制代码 代码如下: int _tmain(int argc,

  • C++编程产生指定范围内的随机数

    C/C++编程产生指定范围内的随机数,直接上个小程序: #include <stdlib.h> #include <time.h> #include <stdio.h> #include <string> #include <string.h> /* * 获取随机数 * return : 随机数 */ int commonGetRandNumber(const int low, const int high) { int randNum = 0;

  • C++实现产生随机数和相应的猜拳小游戏实例代码

    一.简介 c++中,产生随机数的通用方法就是调用 srand()和 rand()  函数. Rand 单纯的rand()会返回一个0至RAND_MAX之间的随机数值,而RAND_MAX的值与int位数有关,最小是32767.不过rand()是一次性的,因为系统默认的随机数种子为1,只要随机数种子不变,其生成的随机数序列就不会改变. 其实,对于rand()的范围,我们是可以进行人为设定的,只需要在宏定义中定义一个random(int x)函数,就可以生成范围为0至x的随机数值.当然,也可以定义为r

  • c++实现简单随机数的代码

    c++简单随机数 #include<iostream> #include<ctime> #include<cstdlib> using namespace std; int random(int n) { return (long long)rand()*rand%n; } int main() { srand(unsigned(time(0))); // //求负数随机数,先产生0-2n之间的随机整数,再减去n就得到了-n - n 之间的数 } 实例扩展: 随机数引擎

  • C++ 随机数字以及随机数字加字母生成的案例

    我就废话不多说了,大家还是直接看代码吧~ #include <time.h> #include <sys/timeb.h> void MainWindow::slot_clicked() { QString strRand; int length = 32; QString strTmp = "1234567890QWERTYUIOPASDFGHJKLZXCVBNM"; struct timeb timer; ftime(&timer); srand(t

  • c++ 随机数问题的相关研究

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

  • Android后台启动Activity的实现示例

    目录 概述 原生Android ROM 定制化ROM 检测后台弹出界面权限 Android P后台启动权限 Android Q后台启动权限 总结 概述 前几天产品提了一个需求,想在后台的时候启动我们 APP 的一个 Activity,随着 Android 版本的更新,以及各家 ROM 厂商的无限改造,这种影响用户体验的功能许多都受到了限制,没办法,虽然是比较流氓的功能,但拿人钱财替人消灾,于是开启了哼哧哼哧的调研之路. 原生Android ROM 首先从 Android 的原生 ROM 开始,根

  • 基于matlab MFCC+GMM的安全事件声学检测系统

    一.安全事件声学检测简介(附lunwen) 1 选题背景 公共安全问题是社会安全稳定所聚焦的话题之一.近年来,检测技术与监控自动化正深刻地改变着人们的生活.尤其在安防领域,闭路电视CCTV(Closed Circuit Television).视频流分析.智能监控等新技术得到了广泛应用,大大提高了安防监控的管理效率.然而值得注意的是,基于视频流的监控手段不可避免地也具有一定的先天性缺漏,例如存在视野盲区.易受光照影响等问题,对于事件检测,还可能存在语义不明的问题,监控手段不够全面.纯视频手段在枪

  • 解析Java定时任务的选型及改造问题

    目录 [前言] [比一比&改一改] 一.项目目前定时任务现状 二.Java主流三大定时任务框架优缺点 三.xxl-job一些特性 四.项目中加入xxl-job结合 [总结] [前言] 项目中用到了定时任务,项目之初为了快速开发上线,当时直接采用最简单的Linux自带的crontab;项目逐渐维定下来时,针对定时任务自己进行了相关研究,并根据项目实际情况进行了对比以及相关改造. [比一比&改一改] 一.项目目前定时任务现状 1. 使用Linux系统的crontab直接调用Java服务 2.

  • 基于Matlab LBP实现植物叶片识别功能

    目录 一.LBP简介 1.1 课题的提出与研究意义 1.2 国内外相关研究情况 1.3 论文的主要研究工作 1.4 论文结构 二.部分源代码 三.运行结果 一.LBP简介 第一章 引言 植物在我们的身边随处可见,它们从产生发展进化到现在,其间经历了漫长的岁月.地球上的植物种类繁多.数量浩瀚,它们是生物圈的重要组成部分,在维持整个生物界的平衡方面发挥着巨大的作用:它们同时也是构成人类生存环境的重要组成部分,是人类社会延续和发展不可或缺的重要因素.由于植物对于地球和人类都具有如此重要的意义,对它们的

  • 图神经网络GNN算法基本原理详解

    目录 前言 1. 数据 2. 变量定义 3. GNN算法 3.1 Forward 3.2 Backward 4.总结与展望 前言 本文结合一个具体的无向图来对最简单的一种GNN进行推导.本文第一部分是数据介绍,第二部分为推导过程中需要用的变量的定义,第三部分是GNN的具体推导过程,最后一部分为自己对GNN的一些看法与总结. 1. 数据 利用networkx简单生成一个无向图: # -*- coding: utf-8 -*- """ @Time : 2021/12/21 11:

  • OpenCV实现图像细化算法

    目录 1.基础概念 2.细化过程 3.代码实现 4.实验结果 1.基础概念 图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization)的一种操作运算.细化是将图像的线条从多像素宽度减少到单位像素宽度过程的简称,一些文章经常将细化结果描述为“骨架化”.“中轴转换”和“对称轴转换”. 细化技术的一个主要应用领域是位图矢量化的预处理阶段,相关研究表明,利用细化技术生成的位图的骨架质量受到多种因素的影响,其中包括图像自身的噪声.线条粗细不均匀.端点的确

  • 朋友网关于QQ相关的PHP代码(研究QQ的绝佳资料)

    复制代码 代码如下: <? /*************************************** ****************************************/ error_reporting(E_ALL ^ E_NOTICE); require_once( 'http.inc.php' ); require_once( 'class.Chinese.php'); //成功2xx     define( 'QQ_RETURN_SUCCESS',    200 );

  • JAVA及相关字符集编码问题研究分享

    下面本文将针对以上几点问题进行描述讨论,我们就以"中文"两个字为例来说明,查找相关资料可知"中文"的GB2312编码是"d6d0 cec4",为Unicode编码为"4e2d 6587",UTF编码就是"e4b8ad e69687".(注意,"中文"这两个字没有iso8859-1编码,但可以用iso8859-1编码来"表示"). 一.编码基本知识: 最早的编码是iso

  • C/C++中指针和引用之相关问题深入研究

    一.基本知识指针和引用的声明方式:声明指针: char* pc;声明引用: char c = 'A'   char& rc = c; 它们的区别:①从现象上看,指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变.这句话可以理解为:指针可以被重新赋值以指向另一个不同的对象.但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变. ②从内存分配上看,程序为指针变量分配内存区域,而不为引用分配内存区域,因为引用声明时必须初始化,从而指向一个已经存在的对

随机推荐