zoukankan      html  css  js  c++  java
  • C/C++中如何产生伪随机数

      1. C语言中的伪随机数产生函数

      本节主要参考自一博文cppreferrence.

      我们知道rand()函数可以用来产生随机数,但是这不是真正意义上的随机数,是一个伪随机数,是根据一个数(我们可以称它为种子)为基准以某个递推公式推算出来的一系列数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统。

       1.1 rand()

      功能:随机数发生器

      用法:int rand(void)

      所在头文件: stdlib.h

      rand()的内部实现是用线性同余法做的,它不是真的随机数,因其周期特别长,故在一定的范围里可看成是随机的。

      rand()返回一随机数值的范围在0至RAND_MAX 间。RAND_MAX的范围最少是在32767之间(int)。用unsigned int 双字节是65535,四字节是4294967295的整数范围。0~RAND_MAX每个数字被选中的机率是相同的。

      用户未设定随机数种子时,系统默认的随机数种子为1。

      rand()产生的是伪随机数字,每次执行时是相同的;若要不同,用函数srand()初始化它。

       1.2 srand()

      功能:初始化随机数发生器

      用法: void srand(unsigned int seed)

      所在头文件: stdlib.h

      srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。

       1.3 使用当前时钟作为随机数种子

      rand()产生的随机数在每次运行的时候都是与上一次相同的。若要不同,用函数srand()初始化它。可以利用srand((unsigned int)(time(NULL))的方法,产生不同的随机数种子,因为每一次运行程序的时间是不同的。

       1.4 产生随机数的用法

      1)给srand()提供一个种子,它是一个unsigned int类型; 
      2)调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到RAND_MAX之间); 
      3)根据需要多次调用rand(),从而不间断地得到新的随机数; 
      4)无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。

      0~RAND_MAX之间的随机数程序

     1 #include <iostream> 
     2 #include <stdlib.h> 
     3 #include <time.h>  
     4 using namespace std;
     5 int main()
     6 {
     7     srand((unsigned)time(NULL));
     8     for (int i = 0; i < 10; i++)
     9         cout << rand() << '	';
    10     cout << endl;
    11 
    12     return 0;
    13 }
    View Code

       1.5 产生一定范围随机数的通用表示公式

      要取得[a, b)的随机整数,使用(rand() % (b-a))+ a; 
      要取得[a, b]的随机整数,使用(rand() % (b-a+1))+ a; 
      要取得(a, b]的随机整数,使用(rand() % (b-a))+ a + 1; 
      通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 
      要取得a到b之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。 
      要取得0~1之间的浮点数,可以使用rand() / double(RAND_MAX)。

      2. C++语言中的伪随机数产生器

      本节主要参考自一博文cpluspluscppreferrence。其中,该博文是对cplusplus上该伪随机数条目的翻译,下文中会参考调整。

      C++中伪随机数库Random是C++11才开始添加的。它允许我们结合生成器(Generators)和分布器(Distributions)来生成伪随机数。 

      生成器(Generators):能够产生离散的等可能分布数值。
      分布器(Distributions): 能够把生成器产生的均匀分布值映射到其他各种各样的分布,如均匀分布uniform,正态分布normal,二项分布binomial,泊松分布poisson。

       2.1 一个简单的例子

      下边就是一个简单的例子:
     1 #include <iostream>
     2 #include <random>
     3 using namespace std;
     4 
     5 int main()
     6 {
     7     default_random_engine generator;
     8     uniform_int_distribution<int> dis(0, 6);
     9     for (int i = 0; i < 6; i++)
    10     {
    11         std::cout << dis(generator) << std::endl;    // 1 0 1 1 2 6
    12     }
    13 
    14     return 0;
    15 }
    View Code

      如果我们嫌每次都要传入生成器对象麻烦,我们可以使用std::bind来绑定生成器对象和分布器对象(注意bind在头文件functional中)。如下所示:

     1 #include <iostream>
     2 #include <random>
     3 #include <functional>    // std::bind
     4 using namespace std;
     5 
     6 int main()
     7 {
     8     default_random_engine generator;
     9     uniform_int_distribution<int> dis(0, 6);
    10     auto dice = bind(dis, generator);
    11     for (int i = 0; i < 6; i++)
    12     {
    13         std::cout << dice() << std::endl;    // 1 0 1 1 2 6
    14     }
    15 
    16     return 0;
    17 }
    View Code

      其实我们会发现上边两个代码的结果不管跑多少次都是一样的,很奇怪!我们可以试着给生成器一个种子,如下:

     1 #include <iostream>
     2 #include <random>
     3 #include <functional>    // std::bind
     4 using namespace std;
     5 
     6 int main()
     7 {
     8     default_random_engine generator(10);    // seed = 10
     9     uniform_int_distribution<int> dis(0, 6);
    10     auto dice = bind(dis, generator);
    11     for (int i = 0; i < 6; i++)
    12     {
    13         std::cout << dice() << std::endl;    // 2 0 4 1 2 4
    14     }
    15 
    16     return 0;
    17 }
    View Code

      现在我们看到结果不一样了。但是,不管我们再跑多少次,其结果还是一样的,这就更加奇怪了。究其原因,在于我们设定的种子是固定的。对于计算机而言,当输入确定时,输出也一定是确定的。因为生成器算法和分布器算法都是确定的,所以当输入是确定的时候,输出也必然是确定的。因此,为了得到不一样的结果,我们可以将系统时间当作种子:

     1 #include <iostream>
     2 #include <random>
     3 #include <functional>    // std::bind
     4 #include "time.h"        // time
     5 using namespace std;
     6 
     7 int main()
     8 {
     9     default_random_engine generator(time(NULL));
    10     uniform_int_distribution<int> dis(0, 6);
    11     auto dice = bind(dis, generator);
    12     for (int i = 0; i < 6; i++)
    13     {
    14         std::cout << dice() << std::endl;
    15     }
    16 
    17     return 0;
    18 }
    View Code

       2.2 关于生成器和分布器

      关于生成器和分布器,请参照cplusplus手册。

      下边是一个来自cppreferrence的例子:
     1 #include <iostream>
     2 #include <iomanip>
     3 #include <string>
     4 #include <map>
     5 #include <random>
     6 #include <cmath>
     7 using namespace std;
     8 
     9 int main()
    10 {
    11     // Seed with a real random value, if available
    12     std::random_device rd;
    13     cout << rd() << endl;
    14     // Choose a random mean between 1 and 6
    15     std::default_random_engine e1(rd());
    16     std::uniform_int_distribution<int> uniform_dist(1, 6);
    17     int mean = uniform_dist(e1);
    18     std::cout << "Randomly-chosen mean: " << mean << '
    ';
    19 
    20     // Generate a normal distribution around that mean
    21     std::mt19937 e2(rd());
    22     std::normal_distribution<> normal_dist(mean, 2);    // default: double
    23 
    24     std::map<int, int> hist;
    25     for (int n = 0; n < 10000; ++n) {
    26         ++hist[std::round(normal_dist(e2))];
    27     }
    28     std::cout << "Normal distribution around " << mean << ":
    ";
    29     for (auto p : hist) {
    30         std::cout << std::fixed << std::setprecision(1) << std::setw(2)
    31             << p.first << ' ' << std::string(p.second / 200, '*') << '
    ';
    32     }
    33 
    34     return 0;
    35 }
    View Code

      我们可以发现上述例子用到了random_device这个生成器。该生成器产生的是非确定性随机数。在Linux的实现中,是读取/dev/urandom设备;在Windows的实现居然是用rand_s。random_device提供()操作符,用来返回一个min()到max()之间的一个数字(min()、max()均为该生成器成员函数)。如果是Linux(Unix Like或者Unix)下,都可以使用这个来产生高质量的随机数,可以理解为真随机数。

      一个例子如下:
     1 // random_device example
     2 #include <iostream>
     3 #include <random>
     4 
     5 int main ()
     6 {
     7   std::random_device rd;
     8 
     9   std::cout << "default random_device characteristics:" << std::endl;
    10   std::cout << "minimum: " << rd.min() << std::endl;
    11   std::cout << "maximum: " << rd.max() << std::endl;
    12   std::cout << "entropy: " << rd.entropy() << std::endl;
    13   std::cout << "a random number: " << rd() << std::endl;
    14 
    15   return 0;
    16 }
    View Code

      可能的输出是:

    1 default random_device characteristics:
    2 minimum: 0
    3 maximum: 4294967295
    4 entropy: 32
    5 a random number: 4230014324
    View Code

      有些情况下,随了拿时间当种子,random_device也经常被用来生成种子。

     
  • 相关阅读:
    .net程序调用检测和性能分析工具——DotTrace
    HR系统邮件审批功能总结
    添加AD验证(域身份验证)到现有网站
    【事务】:数据库事务隔离级别、脏读、幻读、不可重复读
    【TensorFlow】:解决TensorFlow的ImportError: DLL load failed: 动态链接库(DLL)初始化例程失败
    【Anaconda】:科学计算的Python发行版
    【Junit4】:要点随笔
    【ElasticSearch】:elasticsearch.yml配置
    【ElasticSearch】:Windows下ElasticSearch+版本安装head
    ArrayList、Vector、HashMap、HashTable、HashSet的默认初始容量、加载因子、扩容增量
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4355442.html
Copyright © 2011-2022 走看看