C++ 11:如何使用<random>设置种子

Arg*_*ent 15 random c++11

我正在运用随机库,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);
}
Run Code Online (Sandbox Code Playgroud)

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

>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538
Run Code Online (Sandbox Code Playgroud)

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

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

我很感激有关如何让程序在每次调用时生成新种子的建议:越简单就越好.

我的平台:

g24*_*24l 11

具有a的要点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);
Run Code Online (Sandbox Code Playgroud)

如果您的系统没有随机设备,那么您可以将其time(0)用作random_engine的种子

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

如果您有多个随机源,您实际上可以这样做(例如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);
Run Code Online (Sandbox Code Playgroud)

其中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);
Run Code Online (Sandbox Code Playgroud)

最后,我想用一个衬垫初始化:

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

如果您担心time(0)具有第二精度,您可以high_resolution_clock通过请求以下bames23首先指定的纪元以来的时间来解决这个问题:

static_cast<long unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count()) 
Run Code Online (Sandbox Code Playgroud)

或者只是玩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]);
}
Run Code Online (Sandbox Code Playgroud)


bam*_*s53 7

#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);
}
Run Code Online (Sandbox Code Playgroud)

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

A seed_seq可用于初始化多个随机数引擎; seed_seq每次使用时都会生成相同的种子数据.

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


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

  • 高分辨率时钟,例如std::chrono::high_resolution_clock(time()通常分辨率为1秒,通常太低)
  • 由于地址空间布局随机化(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);
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是 Windows 上的 libstdc++ 回退到确定性实现。他们还没有尝试连接到 Windows 的操作系统工具以实现不确定性(当然,Windows 不提供 libstdc++ 在 *nix 平台上使用的工具)。如果您使用 VS2015 或 Linux 上的 gcc 构建程序,那么您将获得不重复的结果。如果你想继续在 Windows 上使用 gcc,那么你需要用类似使用 Windows CryptoAPI 的东西替换 `random_device`。 (2认同)