这是获取随机数的好方法吗?如果不是,为什么?

时间:2015-11-08 00:31:17

标签: c++ random

所以我试图创建一个“真正的”随机函数,因为rand不是随机的imo。

你认为这是获得随机数的“好方法”吗?如果不是,为什么?

int realrand() {
  double duration = 0;
  int i = 0;
  std::clock_t start;
  start = std::clock();

  while(duration < 0.001) {
    i++;
    duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
  }

  int rand = i % 10;

  return rand;
}

2 个答案:

答案 0 :(得分:7)

两件事。

  1. 您的随机数生成器真的慢。

    在我的笔记本电脑上计算C ++ 11和C样式rng 10000次约需0.016秒。将代码添加到混合中会将成本提高到 10秒

  2. 您正在创建的随机数的分布很糟糕。

             You        C++11      rand()
    
    0:       9615       9007       9006
    1:       9143       9086       9008
    2:       8650       8929       8958
    3:       7907       9094       9114
    4:       7943       9022       9021
    5:       8098       8968       9005
    6:       8676       8991       8810
    7:       9970       8946       8903
    8:      10061       8908       9094
    9:       9938       9050       9082
    

    让我们在直方图中显示这些数据。请注意,C和C ++实现在每个存储桶中具有大约相同数量的元素。另一方面,您的代码显然更喜欢输出0,7,8或9。

  3. Histogram of the above data

    以下是我用来确定这个问题的一些代码:

    #include <random>
    #include <map>
    
    int realrand() {
            double duration = 0;
            int i = 0;
            std::clock_t start;
            start = std::clock();
    
            while(duration < 0.001) {
                    i++;
                    duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
            }
    
            int rand = i % 10;
    
            return rand;
    }
    
    int cpprand() {
            static std::random_device rd;
            static std::mt19937 rng(rd());
            static std::uniform_int_distribution<> dist(0, 9);
            return dist(rng);
    }
    
    int crand() {
            static int once = []() {
                    srand(time(NULL));
                    return 0;
            }();
    
            return rand() % 10;
    }
    
    int main() {
            std::map<int, int> realrand_distribution;
            std::map<int, int> cpprand_distribution;
            std::map<int, int> crand_distribution;
    
            for (int i=0; i<10000; ++i) {
                    realrand_distribution[realrand()]++;
                    cpprand_distribution[cpprand()]++;
                    crand_distribution[crand()]++;
            }
    
            for (int i=0; i<10; ++i) {
                    printf("%d: %10d %10d %10d\n", i, realrand_distribution[i], cpprand_distribution[i], crand_distribution[i]);
            }
    }
    

    这是我用来生成直方图的代码:

    import numpy
    import pylab
    
    x = numpy.arange(10)
    width = 0.27
    
    rects1 = pylab.bar(x+0*width, [9615, 9143, 8650, 7907, 7943, 8098, 8676, 9970, 10061, 9938], width, color='r')
    rects2 = pylab.bar(x+1*width, [9007, 9086, 8929, 9094, 9022, 8968, 8991, 8946, 8908,  9050], width, color='b')
    rects3 = pylab.bar(x+2*width, [9006, 9008, 8958, 9114, 9021, 9005, 8810, 8903, 9094,  9082], width, color='g')
    pylab.legend([rects1, rects2, rects3], ["You", "C++", "C"])
    pylab.show()
    

答案 1 :(得分:1)

要获得随机数(优于rand() / random() / etc),您需要使用库。有很多可用的东西,而C ++使一些基本的东西变得容易。

如上所述,std::random_device存在问题。不幸的是,比人们意识到的更多

如果你想要真正随机的数字,你现在最好使用特定于系统的方法来获取它们:Mac和Linux上的/dev/urandom以及Windows上的CryptoAPI。

你可以轻松地使用TRNG初始化(“种子”)PRNG,如XORShift,Mersenne Twister等。如果你需要CSPRNG,请使用加密库,但是对于其他所有这些都可以。

如果可以避免,请不要从时钟播种。它远没有人们想象的那么“随机”。无论你做什么,都要忽略使用带有PID等的哈希值的废话。

希望这有帮助。