C++11 保存c+的状态+;11不使用iostream的随机生成器

C++11 保存c+的状态+;11不使用iostream的随机生成器,c++11,random,C++11,Random,不使用iostream接口存储C++11随机生成器状态的最佳方法是什么。我想要这里列出的第一个备选方案[1]?但是,这种方法要求对象包含PRNG状态,并且仅包含PRNG状态。在partucular中,如果实现使用pimpl模式(至少在重新加载状态而不是用坏数据加载状态时,这可能会使应用程序崩溃),或者有更多与PRNG对象关联的状态变量与生成的序列无关,则会失败 对象的大小由实现定义: g++(tdm64-1)4.7.1给出了sizeof(std::mt19937)==2504 Ideone25

不使用iostream接口存储C++11随机生成器状态的最佳方法是什么。我想要这里列出的第一个备选方案[1]?但是,这种方法要求对象包含PRNG状态,并且仅包含PRNG状态。在partucular中,如果实现使用pimpl模式(至少在重新加载状态而不是用坏数据加载状态时,这可能会使应用程序崩溃),或者有更多与PRNG对象关联的状态变量与生成的序列无关,则会失败

对象的大小由实现定义:

  • g++(tdm64-1)4.7.1
    给出了
    sizeof(std::mt19937)==2504
  • Ideone
    2500
我缺少像这样的成员函数

  • size\u t state\u size()
  • const size\u t*get\u state()const
  • void set\u state(大小元素,常量大小*状态新)
  • (1) 应返回随机生成器状态数组的大小

    (2) 应返回指向状态数组的指针。指针由PRNG管理

    (3) 应将缓冲区
    std::min(n元素,state\u size())
    从state\u new指向的缓冲区复制

    这种接口允许更灵活的状态操作。或者是否存在状态不能表示为无符号整数数组的PRNG:s

    [1]

    我已经为我在评论中提到的方法编写了一个简单的(-ish)测试。它显然没有经过战斗测试,但它代表了一个想法——你应该可以从这里开始

    由于读取的字节数比序列化整个引擎要小得多,因此这两种方法的性能实际上可能相当。检验这个假设,以及进一步优化,留给读者作为练习

    #include <iostream>
    #include <random>
    #include <chrono>
    #include <cstdint>
    #include <fstream>
    
    using namespace std;
    
    struct rng_wrap
    {
        // it would also be advisable to somehow
        // store what kind of RNG this is,
        // so we don't deserialize an mt19937
        // as a linear congruential or something,
        // but this example only covers mt19937
    
        uint64_t seed;
        uint64_t invoke_count;
        mt19937 rng;
    
        typedef mt19937::result_type result_type;
    
        rng_wrap(uint64_t _seed) :
            seed(_seed),
            invoke_count(0),
            rng(_seed)
        {}
    
        rng_wrap(istream& in) {
            in.read(reinterpret_cast<char*>(&seed), sizeof(seed));
            in.read(reinterpret_cast<char*>(&invoke_count), sizeof(invoke_count));
            rng = mt19937(seed);
            rng.discard(invoke_count);
        }
    
        void discard(unsigned long long z) {
            rng.discard(z);
            invoke_count += z;
        }
    
        result_type operator()() {
            ++invoke_count;
            return rng();
        }
    
        static constexpr result_type min() {
            return mt19937::min();
        }
    
        static constexpr result_type max() {
            return mt19937::max();
        }
    };
    
    ostream& operator<<(ostream& out, rng_wrap& wrap)
    {
        out.write(reinterpret_cast<char*>(&(wrap.seed)), sizeof(wrap.seed));
        out.write(reinterpret_cast<char*>(&(wrap.invoke_count)), sizeof(wrap.invoke_count));
        return out;
    }
    
    istream& operator>>(istream& in, rng_wrap& wrap)
    {
        wrap = rng_wrap(in);
        return in;
    }
    
    void test(rng_wrap& rngw, int count, bool quiet=false)
    {
        uniform_int_distribution<int> integers(0, 9);
        uniform_real_distribution<double> doubles(0, 1);
        normal_distribution<double> stdnorm(0, 1);
    
        if (quiet) {
            for (int i = 0; i < count; ++i)
                integers(rngw);
    
            for (int i = 0; i < count; ++i)
                doubles(rngw);
    
            for (int i = 0; i < count; ++i)
                stdnorm(rngw);
        } else {
            cout << "Integers:\n";
            for (int i = 0; i < count; ++i)
                cout << integers(rngw) << " ";
    
            cout << "\n\nDoubles:\n";
            for (int i = 0; i < count; ++i)
                cout << doubles(rngw) << " ";
    
            cout << "\n\nNormal variates:\n";
            for (int i = 0; i < count; ++i)
                cout << stdnorm(rngw) << " ";
            cout << "\n\n\n";
        }
    }
    
    
    int main(int argc, char** argv)
    {
        rng_wrap rngw(123456790ull);
    
        test(rngw, 10, true);  // this is just so we don't start with a "fresh" rng
        uint64_t seed1 = rngw.seed;
        uint64_t invoke_count1 = rngw.invoke_count;
    
        ofstream outfile("rng", ios::binary);
        outfile << rngw;
        outfile.close();
    
        cout << "Test 1:\n";
        test(rngw, 10);  // test 1
    
        ifstream infile("rng", ios::binary);
        infile >> rngw;
        infile.close();
    
        cout << "Test 2:\n";
        test(rngw, 10);  // test 2 - should be identical to 1
    
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间std;
    结构rng_包装
    {
    //还建议以某种方式
    //存储这是什么类型的RNG,
    //所以我们不反序列化mt19937
    //作为一个线性同余或什么的,
    //但本例仅涵盖mt19937
    uint64_t种子;
    uint64\u t调用\u计数;
    mt19937 rng;
    typedef mt19937::result_type result_type;
    rng_包装(uint64_t_种子):
    种子,
    调用_计数(0),
    rng(_种子)
    {}
    rng_包裹(istream&in){
    in.read(重新解释类型(&seed)、sizeof(seed));
    in.read(reinterpret_cast(&invoke_count))、sizeof(invoke_count));
    rng=mt19937(种子);
    废弃(调用计数);
    }
    无效放弃(无符号长z){
    废弃(z);
    调用_count+=z;
    }
    结果类型运算符(){
    ++调用u计数;
    返回rng();
    }
    静态constexpr结果_type min(){
    返回mt19937::min();
    }
    静态constexpr结果_type max(){
    返回mt19937::max();
    }
    };
    ostream和操作员(istream和in、rng_包裹和包裹)
    {
    包裹=rng_包裹(英寸);
    返回;
    }
    无效测试(rng\U包裹和rngw,整数计数,布尔安静=假)
    {
    均匀分布整数(0,9);
    均匀实双分布(0,1);
    正态分布stdnorm(0,1);
    如果(安静){
    对于(int i=0;i除此之外,我认为序列化一个RNG(或者任何对象,真的)是不可能的没有关于底层实现的一些知识。如果可能的话,它可能会涉及一些……古怪的黑客行为。@MoreAxes提到的问题是不相关的。此外,这实际上不是一个性能问题,而是一个接口兼容性问题:I/O接口不是从任何iostream类派生的,我不能使用提供的方法没有首先复制到stringstream,然后转换回二进制,最后使用ChunkIO::Writer::dataWrite函数写入。g++
    和Ideone的
    sizeof std::mt19937
    返回的值分别比存储Mersene Twister的状态数组所需的值大8字节和4字节。如果在任何一种情况下都是一个值,那么我敢打赌它是一个指针(我假设您使用的是64位系统,而Ideone不是),您需要在序列化过程中对其进行相应处理,可以安全地按原样序列化。您也可以尝试另一种方法:创建一个包装类来存储种子和调用次数,并仅存储这些。在反序列化过程中,使用存储的种子为RNG种子,并多次调用它。这可能有点不可靠,因为我不确定C++11的发行版是否总是请求调用RNG时使用的伪随机字节数与调用RNG时相同,但似乎值得一试。在反序列化过程中显然会非常慢,但序列化会非常快。@此外,这是一个大小值:
    \u UIntType\u M\u x[状态大小];size_t_M_p;
    \u M_p
    是状态大小,也会写入序列化。生成1e6随机数需要多长时间?