c++ 随机数问题的相关研究
1、问题背景
某项目中有个复杂的排序,先是各种规则依次排序,最后如果依然并列的话,那就随机位置,名次并列。测试中发现一个诡异现象,并列时随机排序但随机后2个case打印的顺序每次都一样,随机数没有起到任何作用。经过分析发现,随机数种子srand(clock()),本意是希望连续调用这个函数,给多个随机数设置种子,实际上设置的种子相同,最后产生的随机数是伪随机数。那么有没有一种随机数方法可以在较快的循环中,保证随机性呢?
原问题较复杂,给个类似的例子说明具体场景:
voidtest_random() { vectorvec; vec.resize(100); iota(vec.begin(),vec.end(),1); vector vec2(vec); srand(clock()); random_shuffle(vec.begin(),vec.end()); srand(clock()); random_shuffle(vec2.begin(),vec2.end()); intcnt=0; cout<<"vec:"< 输出结果为:
2、rand()和srand()
rand()和srand()是c函数,在stdlib.h中定义,rand()能产生0--32767范围的随机数。
如果只使用rand,则每次输出的随机数都是一样的,相当于使用srand(1)作为默认种了。如果给定种了,则能产生不同的随机数,所以time或clock函数就是一个好种子,获取计算机的时间,用秒或毫秒来做随机数种子以产生不同的随机数。但是在某些场景下,会引发下列问题:
问题1:在程序运行较慢或不需要连续产生随机数时,用时钟当做种子没有问题,但要快速产生不同的组数的随机数时,就会出现前面出现的现象,较大概率出现相同的随机数。
问题2:如果希望生成某个范围的随机数,则不好控制,通常会采用取模的方式,而这种方式会破坏随机数的分布概率。
//0--10的随机数 srand((unsignedint)time(NULL)); intr=rand()%10 //100--200的随机数 intmin=100; intmax=200; srand((unsignedint)time(NULL)); intr=rand()%(max-min)+min //[0--1.0]浮点数 srand((unsignedint)time(NULL)); floatr=rand()%RAND_MAX3、c++11随机数
c++11引入了random头文件,可以更加精确的产生随机数,并且提供了完善的操作接口。C++标准规定了随机数设施,包括均匀随机位生成器(Uniformrandombitgenerators,URBG)和随机数分布等,定义在
中。 参考文档:http://www.cplusplus.com/reference/random/?kw=random
Thislibraryallowstoproducerandomnumbersusingcombinationsofgeneratorsanddistributions:
Generators:Objectsthatgenerateuniformlydistributednumbers.
Distributions:Objectsthattransformsequencesofnumbersgeneratedbyageneratorintosequencesofnumbersthatfollowaspecificrandomvariabledistribution,suchasuniform,NormalorBinomial.
random标准款主要包括:
生成器:生成均匀分布伪随机数的对象
分布:将生成器生成的数序列转换为某种特定数学概率分布的序列,如均匀分布、正态分布、泊松分布等。
3.1、生成器
1)random_device生成器
C++11提供了一个random_device随机数类,英文叫“Non-deterministicrandomnumbergenerator”,这是一个非确定性随机数生成器,它并不是由某一个数学算法得到的随机序列,而是通过读取文件,读什么文件看具体的实现(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-blockrandomnumberengineadaptor(classtemplate)independent_bits_engine:Independent-bitsrandomnumberengineadaptor(classtemplate)shuffle_order_engine:Shuffle-orderrandomnumberengineadaptor(classtemplate)
3.3、随机分布模板类
随机数引擎产生的随机数值都比较大,使用时经常需要限定到一个范围内,c++11提供了符合各种概率分布的随机数生成模板类,比如:均匀分布,正态分布,泊松分布等。
以均匀分布为例:
templateclassuniform_int_distribution; template classuniform_real_distribution; 测试1:直接使用引擎产生随机数,范围很大。
random_devicerd; mt19937g(rd()); for(intn=0;n<10;++n) { cout<测试2:使用均匀分布类模板产生随机数,可以限定生成的随机数的范围。
random_devicerd; mt19937g(rd()); uniform_int_distribution<>dis(1,100); for(intn=0;n<10;++n) { cout<3.4、用法总结
1、定义种子,可以是随机种子或者固定种子,固定种子方便测试用,但每次产生的随机数都一致。
2、选择随机引擎,把种子值传入当做参数。
3、选择合适分布方式,创建随机分布对象,可以在此时指定需要的随机数的范围。
4、把引擎传入随机数分布模板类对象,输出随机数。
4、问题解决
c++提供了一个shuffle函数,相比于random_shuffle,shuffle可以指定随机数引擎,如果指定一个非确定性引擎,则能保证连续生成的两组随机数各不相同,达到设计效果。
templatevoidshuffle(_RanIt_First,_RanIt_Last,_Urng&&_Func) 修改后的测试函数:
voidtest_random() { vectorvec; vec.resize(100); iota(vec.begin(),vec.end(),1); vector vec2(vec); autoengine=std::default_random_engine(std::random_device()()); shuffle(vec.begin(),vec.end(),engine); shuffle(vec2.begin(),vec2.end(),engine); intcnt=0; cout<<"vec:"< 上面例子usingdefault_random_engine=mt19937;其中,mt19937是一个引擎,最大值为0Xffffffff。
usingmt19937=mersenne_twister_engine
; 输出结果为:
vec: 857588291760578171 8293447844065793724 3143625321691488638 63788028443934906913 7417759884146563362 2118305289228727953 7051272924226667397 154331491006854351299 667596948310456150 23761998115575209564 vec2: 3751126299956517829 80134872832325759768 8640243084447287657 3338161869970314249 52719196817334451026 2938941546444223639 8743635533227198579 35558115659218815100 745381460921750790 6206777986166824694c++随机数问题研究
原创首发:https://www.cnblogs.com/pingwen/p/14496607.html
以上就是c++随机数问题的相关研究的详细内容,更多关于c++随机数问题研究的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。