<random>在Linux中生成相同的编号,但在Windows中不生成

Ami*_*bah 88 c++ linux windows gcc visual-studio-2013

下面的代码用于生成区间[1,100]中的五个伪随机数的列表.我播种default_random_enginewith time(0),它在unix时间内返回系统时间.当我使用Microsoft Visual Studio 2013在Windows 7上编译和运行此程序时,它按预期工作(见下文).但是,当我使用g ++编译器在Arch Linux中这样做时,它表现得很奇怪.

在Linux中,每次都会生成5个数字.最后4个数字在每次执行时都会有所不同(通常情况就是如此),但第一个数字将保持不变.

Windows和Linux上5次执行的示例输出:

      | Windows:       | Linux:        
---------------------------------------
Run 1 | 54,01,91,73,68 | 25,38,40,42,21
Run 2 | 46,24,16,93,82 | 25,78,66,80,81
Run 3 | 86,36,33,63,05 | 25,17,93,17,40
Run 4 | 75,79,66,23,84 | 25,70,95,01,54
Run 5 | 64,36,32,44,85 | 25,09,22,38,13
Run Code Online (Sandbox Code Playgroud)

除此之外,第一个数字在Linux上定期递增1.在获得上述输出后,我等了大约30分钟并再次尝试发现第一个数字已经改变,现在总是以26为单位生成.它继续定期增加1,现在是32.它似乎对应随着价值的变化time(0).

为什么第一个数字很少在运行中发生变化,然后在运行时,增加1?

代码.它整齐地打印出5个数字和系统时间:

#include <iostream>
#include <random>
#include <time.h>

using namespace std;

int main()
{
    const int upper_bound = 100;
    const int lower_bound = 1;

    time_t system_time = time(0);    

    default_random_engine e(system_time);
    uniform_int_distribution<int> u(lower_bound, upper_bound);

    cout << '#' << '\t' << "system time" << endl
         << "-------------------" << endl;

    for (int counter = 1; counter <= 5; counter++)
    {
        int secret = u(e);
        cout << secret << '\t' << system_time << endl;
    }   

    system("pause");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 139

这是发生了什么:

  • default_random_engine在libstdc ++(GCC的标准库)中minstd_rand0,这是一个简单的线性同余引擎:

    typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
    
    Run Code Online (Sandbox Code Playgroud)
  • 该引擎生成随机数的方式是x i + 1 =(16807x i + 0)mod 2147483647.

  • 因此,如果种子相差1,那么大多数时候第一个生成的数字将相差16807.

  • 该发生器的范围是[1,2147483646].libstdc ++ uniform_int_distribution将它映射到[1,100]范围内的整数的方式基本上是这样的:生成一个数字n.如果数量不大于2147483600,则返回(n - 1) / 21474836 + 1; 否则,请尝试使用新号码.

    应该很容易看出,在绝大多数情况下,两个n仅相差16807的s将在[1,100]中产生相同的数量.实际上,人们会期望生成的数字每21474836/16807 = 1278秒或21.3分钟增加一,这与您的观察结果非常吻合.

MSVC default_random_enginemt19937,没有这个问题.

  • 我想知道GCC标准库的开发人员选择这样一个可怕的默认值是什么. (35认同)
  • @CodesInChaos我不知道它是否与之相关,但MacOS/iOS工具链也使用相同的可怕随机引擎,使[`rand()`%7总是返回0](http://stackoverflow.com/q/ 995714分之7866754) (12认同)
  • @LưuVĩnhPhúc不修复`rand()`有点可以理解(它是无望的遗留垃圾).将shit-tier PRNG用于新事物是不可原谅的.我甚至认为这是违规行为,因为该标准要求"至少为相对随意,不熟练和/或轻量级的使用提供可接受的引擎行为".这个实现没有提供,因为即使是像你的'rand%7'例子那样的琐碎用例,它也不会发生灾难性的失败. (7认同)
  • @CodesInChaos为什么不修复`rand()`确实可以理解?难道只是因为没有人想过这样做吗? (2认同)
  • @immibis API非常破碎,你可以通过独立的替代品来解决所有问题.1)更换算法将是一个重大变化,因此您可能需要为旧程序进行兼容性切换.2)'srand`的种子太小,不易产生独特的种子.3)它返回一个带有实现定义上限的整数,调用者必须以某种方式将其减少到所需范围内的数字,正确完成后比使用sand API为`rand()`4编写替换更多的工作它使用全局可变状态 (2认同)

Cas*_*sey 28

std::default_random_engine是实现定义.使用std::mt19937std::mt19937_64代替.

此外std::time,ctime功能不是很准确,请使用<chrono>标题中定义的类型:

#include <iostream>
#include <random>
#include <chrono>

int main()
{
    const int upper_bound = 100;
    const int lower_bound = 1;

    auto t = std::chrono::high_resolution_clock::now().time_since_epoch().count();

    std::mt19937 e;
    e.seed(static_cast<unsigned int>(t)); //Seed engine with timed value.
    std::uniform_int_distribution<int> u(lower_bound, upper_bound);

    std::cout << '#' << '\t' << "system time" << std::endl
    << "-------------------" << std::endl;

    for (int counter = 1; counter <= 5; counter++)
    {
        int secret = u(e);

        std::cout << secret << '\t' << t << std::endl;
    }   

    system("pause");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议使用`std :: random_device`而不是current_time来播种随机生成器.请检查有关Random的任何cppreference示例. (15认同)
  • 如果你不想让任何人猜测你的种子(并因此重现你的序列),那么较低的精度与随机性不同.让我们走向极端:将你的种子转到第二天(或一年?) - >猜测很容易.使用飞秒精度 - >很多猜测...... (5认同)
  • 在播种伪随机变量发生器时,是否需要使用更准确的时间?也许这是天真的,但如果它引入熵,感觉就像是不准确的.(除非你的意思是它不那么精确,因此导致种子的实质性减少.) (3认同)
  • @ChemicalEngineer`ctime`的粒度是1秒.`std :: chrono`实现的粒度是用户定义的,默认为`std :: high_resolution_clock`(在Visual Studio中它是`std :: steady_clock`的typedef),纳秒但可以选择小得多的测量,因此,更精确. (2认同)
  • @linac如果你想要加密属性,你会使用适当的prng(在这个答案中没有使用).当然,无论承诺的精确度如何,基于时间的种子也是不可能的. (2认同)