缘起

整个事情的起因是这样的:

C++第四个实验要生成一组随机序列数,众所周知,rand()函数用的线性同余法生成随机数,但是这个随机数实际上是一个伪随机数。然后我就想用std::random_device来生成真随机数。然而在Windows下,GCC的表现却让我大跌眼镜。编译环境是MinGW gcc5.3.0。

不同的表现

首先是示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
#include <random>

using namespace std;

int main()
{
    random_device rd;
    vector<int> v1, v2;
    while (v1.size() != 20)
        v1.push_back(rd() % 20 + 1);
    while (v2.size() != 20)
        v2.push_back(rd() % 20 + 1);
    for (const auto &i : v1)
        cout << i << " ";
    cout << endl;
    for (const auto &i : v2)
        cout << i << " ";
    cout << endl;
    return 0;
}

GCC的情况

代码本身含义再清晰不过,在Linux下跑出了满意的结果。但是在Windows下居然跑出来这种结果:

1
2
13 3 15 6 5 12 10 6 19 4 16 6 1 7 20 1 20 5 18 7
10 10 4 20 7 7 17 14 1 12 13 10 10 19 7 16 14 14 8 9

是随机数没错啊。然而我们再跑一遍:

1
2
13 3 15 6 5 12 10 6 19 4 16 6 1 7 20 1 20 5 18 7
10 10 4 20 7 7 17 14 1 12 13 10 10 19 7 16 14 14 8 9

???

我就很懵,这东西是不是在Windows下没用的???

MSVC的情况

然后我又用Visual Studio 2015跑了一遍。这是第一次的结果:

1
2
16 14 14 17 13 1 20 4 20 4 1 16 12 6 8 8 8 9 19 1
8 2 6 2 8 17 13 3 19 7 4 7 9 11 5 2 9 20 8 20

这是第二次:

1
2
13 15 12 6 13 5 13 2 8 7 14 13 9 7 15 16 4 20 11 10
6 11 9 17 8 20 15 5 14 9 12 14 5 1 4 20 3 1 19 15

???

img

实现

本着无聊的原则,让我们看一下头文件:

没有什么头文件

实现都在.o里面的,我什么都没找到。

然后查阅了Stack overflow,看到如下问题:

http://stackoverflow.com/questions/18880654/why-do-i-get-the-same-sequence-for-every-run-with-stdrandom-device-with-mingw

我跟着文章在cppreference看到这么一段话:

std::random_device may be implemented in terms of an implementation-defined pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation. In this case each std::random_device object may generate the same number sequence.

那么实际上很明显了,windows下的GCC并没有完整实现std::random_device,所以才会出现多次随机序列一样的情况。(我猜就是直接 proxy 到rand(),种子都不换的)

那么MSVC呢?

http://stackoverflow.com/questions/9549357/the-implementation-of-random-device-in-vs2010

这里讲的很详细了,MSVC利用了很多数据来计算熵,然后使用RtlGenRandom()/SystemFunction036()来计算随机数,可以认为是真随机。

同时,在Linux下,GCC使用读取/dev/urandom的方式实现了std::random_device,这个文件本身就是一个真随机数(实际上,还是伪随机,想了解更多点这个)生成器,也就很完美的实现了。

结论

其实结论到现在就很简单了,如果你要在Windows下编译C程序,请首选MSVC编译器,微软投了这么多人力物力不是白做的。

如果你不想装Visual Studio这么大的IDE,你可以选择Code::Blocks,它可以选择使用MSVC作为编译环境,具体怎么搭独立的MSVC编译环境请使用搜索引擎;你还可以使用Visual Studio Code,这个是微软家做的文本编辑器,不得不说是真的无敌,自带了一份MSVC编译环境,小东西用它写足够了。

Linux下请选择Clang或者GCC(个人强烈推荐Clang,之后会写文章说为什么)。

Windows下真的推荐不要再使用MinGW或者Cygwin了,GCC本身诞生就不是给Windows写的,很多东西支持上不如MSVC。

以上都不要信,喜欢用啥就用啥

反正我是换成MSVC了。

P.S. 写的好水啊,怎么能写出这么水的文章的,还是需要学习一波