【问题标题】:C++11: How to set seed using <random>C++11:如何使用 <random> 设置种子
【发布时间】:2016-04-02 03:07:43
【问题描述】:

我正在使用随机库,它是 C++11 的新手。我编写了以下最小程序:

#include <iostream>
#include <random>
using namespace std;
int main() {
    default_random_engine eng;
    uniform_real_distribution<double> urd(0, 1);
    cout << "Uniform [0, 1): " << urd(eng);
}

当我反复运行它时,它每次都会给出相同的输出:

>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538

我想让程序在每次调用时都设置不同的种子,以便每次生成不同的随机数。我知道 random 提供了一个名为 seed_seq 的工具,但我发现它的解释(在 cplusplus.com 上)完全模糊:

http://www.cplusplus.com/reference/random/seed_seq/

对于如何让程序在每次调用时生成一个新种子,我不胜感激:越简单越好。

我的平台:

【问题讨论】:

    标签: c++11 random


    【解决方案1】:

    拥有seed_seq 的目的是增加生成序列的熵。如果您的系统上有一个 random_device,则使用该随机设备中的多个数字进行初始化可能会做到这一点。在具有伪随机数生成器的系统上,我认为随机性不会增加,即生成的序列熵。

    以您的方法为基础:

    如果您的系统确实提供了随机设备,那么您可以像这样使用它:

      std::random_device r;
      // std::seed_seq ssq{r()};
      // and then passing it to the engine does the same
      default_random_engine eng{r()};
      uniform_real_distribution<double> urd(0, 1);
      cout << "Uniform [0, 1): " << urd(eng);
    

    如果您的系统没有随机设备,那么您可以使用time(0) 作为随机引擎的种子

      default_random_engine eng{static_cast<long unsigned int>(time(0))};
      uniform_real_distribution<double> urd(0, 1);
      cout << "Uniform [0, 1): " << urd(eng);
    

    如果您有多个随机来源,您实际上可以这样做(例如 2)

    std::seed_seq seed{ r1(), r2() };
      default_random_engine eng{seed};
      uniform_real_distribution<double> urd(0, 1);
      cout << "Uniform [0, 1): " << urd(eng);
    

    其中 r1 , r2 是不同的随机设备,例如热噪声量子源

    当然可以混搭

    std::seed_seq seed{ r1(), static_cast<long unsigned int>(time(0)) };
      default_random_engine eng{seed};
      uniform_real_distribution<double> urd(0, 1);
      cout << "Uniform [0, 1): " << urd(eng);
    

    最后,我喜欢使用单行进行初始化:

      auto rand = std::bind(std::uniform_real_distribution<double>{0,1},
                  std::default_random_engine{std::random_device()()});
      std::cout << "Uniform [0,1): " << rand();
    

    如果您担心time(0) 具有第二精度,您可以通过使用high_resolution_clock 来克服这个问题,或者通过请求bames23 below 首先指定的纪元以来的时间:

    static_cast<long unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count()) 
    

    或者只是玩 CPU 随机性

    long unsigned int getseed(int const K)
    {
    
        typedef std::chrono::high_resolution_clock hiclock;
    
        auto gett= [](std::chrono::time_point<hiclock> t0)
        {
            auto tn = hiclock::now();
            return static_cast<long unsigned int>(std::chrono::duration_cast<std::chrono::microseconds>(tn-t0).count());
        };
    
        long unsigned int diffs[10];
        diffs[0] = gett(hiclock::now());
        for(int i=1; i!=10; i++)
        {
            auto last = hiclock::now();
            for(int k=K; k!=0; k--)
            {
                diffs[i]= gett(last);
            }
        }
    
        return *std::max_element(&diffs[1],&diffs[9]);
    }
    

    【讨论】:

    • 谢谢,g241。修改 default_random_engine 的初始化以包含对 time(0) 的调用后,程序输出是不确定的。
    • @Argent ,Windows 上的 AFAIK 没有随机引擎, time(0) 以第二精度将种子初始化为当前,因此后续运行以不同的方式播种并实现您的目标。第二个是你的粒度。如果这个答案解决了你的问题,请采纳。
    • Windows 绝对有随机引擎;默认使用 rand_s(),它使用 RtlGenRandom,它调用 advapi32.dll 来生成相当好的随机数(没有一路加密的成本。)
    • @JonWatte:我怀疑 g24l 正在考虑 MinGW 的问题,至少在 Windows 上,is producing a 100% repeatable, deterministic number stream(截至 2017 年,不知道是否/何时修复)。 MSVC 提供了一个可用的随机引擎,这样的功能一般在 Windows 上都有,但是 libstdc++ MinGW 使用的甚至都没有尝试使用它。
    • 朋友不要让朋友使用MinGW。 Visual Studio 可免费下载。修复警告 VS 发现 g++ 没有,对您的代码有好处!
    【解决方案2】:
    #include <iostream>
    #include <random>
    
    using namespace std;
    
    int main() {
        std::random_device r;                                       // 1
        std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; // 2
        std::mt19937 eng(seed);                                     // 3
    
        uniform_real_distribution<double> urd(0, 1);
    
        cout << "Uniform [0, 1): " << urd(eng);
    }
    

    为了从伪随机数生成器中获得不可预知的结果 我们需要一个不可预测的种子数据来源。在 1 上,我们创建了一个 std::random_device 用于此目的。在 2 我们使用std::seed_seq 来组合 由random_device 生成的几个值转换为适合播种的形式 伪随机数发生器。输入的更不可预测的数据 seed_seq,种子引擎的结果越难以预测 是。在 3 上,我们使用seed_seq 创建一个随机数引擎来播种 引擎的初始状态。

    一个seed_seq可以用来初始化多个随机数引擎; seed_seq 每次使用都会产生相同的种子数据。

    注意: 并非所有实现都提供非确定性数据源。 检查您的实现文档中的std::random_device


    如果您的平台不提供不确定的random_device,则可以使用其他一些来源进行播种。文章Simple Portable C++ Seed Entropy 提出了一些替代来源:

    • 高分辨率时钟,例如 std::chrono::high_resolution_clocktime() 通常具有一秒的分辨率,这通常太低了)
    • 现代操作系统上的内存配置因地址空间布局随机化 (ASLR) 而异
    • CPU 计数器或随机数生成器。 C++ 不提供对这些的标准化访问,所以我不会使用它们。
    • 线程 ID
    • 一个简单的计数器(仅在您多次播种时才重要)

    例如:

    #include <chrono>
    #include <iostream>
    #include <random>
    #include <thread>
    #include <utility>
    
    using namespace std;
    
    // we only use the address of this function
    static void seed_function() {}
    
    int main() {
        // Variables used in seeding
        static long long seed_counter = 0;
        int var;
        void *x = std::malloc(sizeof(int));
        free(x);
    
        std::seed_seq seed{
            // Time
            static_cast<long long>(std::chrono::high_resolution_clock::now()
                                       .time_since_epoch()
                                       .count()),
            // ASLR
            static_cast<long long>(reinterpret_cast<intptr_t>(&seed_counter)),
            static_cast<long long>(reinterpret_cast<intptr_t>(&var)),
            static_cast<long long>(reinterpret_cast<intptr_t>(x)),
            static_cast<long long>(reinterpret_cast<intptr_t>(&seed_function)),
            static_cast<long long>(reinterpret_cast<intptr_t>(&_Exit)),
            // Thread id
            static_cast<long long>(
                std::hash<std::thread::id>()(std::this_thread::get_id())),
            // counter
            ++seed_counter};
    
        std::mt19937 eng(seed);
    
        uniform_real_distribution<double> urd(0, 1);
    
        cout << "Uniform [0, 1): " << urd(eng);
    }
    

    【讨论】:

    • 你能在代码中添加更多的 cmets 来详细说明一下吗? :)
    • bames53:我编译并运行了你的代码。我继续获得确定性输出,即每次都相同。可能是我的平台有问题。它是 Windows 7,我正在运行 TDM-GCC 编译器。
    • 问题在于 Windows 上的 libstdc++ 回退到确定性实现。他们还没有尝试使用 Windows 的操作系统工具来实现非确定性(当然 Windows 不提供 libstdc++ 在 *nix 平台上使用的工具)。如果您在 Linux 上使用 VS2015 或 gcc 构建程序,那么您将获得不重复的结果。如果您想继续在 Windows 上使用 gcc,则需要将 random_device 替换为使用 Windows CryptoAPI 之类的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2014-09-14
    相关资源
    最近更新 更多