Save state of random C ++ 11 generator without using iostream

What is the best way to store the state of a random C ++ 11 generator without using the iostream interface. I would like to do as the first alternative listed here [1]? However, this approach requires that the object contains the PRNG state and only the PRNG state. In partucular, it fails if the implementation uses the pimpl template (at least, it can cause the application to crash when the state reloads rather than loading it with bad data), or there are more state variables associated with the PRNG object that does not have do with the created sequence.

The size of the object is determined by the implementation:

I miss member functions like

  • size_t state_size();
  • const size_t* get_state() const;
  • void set_state(size_t n_elems,const size_t* state_new);

(1) should return the size of an array of states of a random generator

(2) returns a pointer to an array of states. The pointer is managed by PRNG.

(3) copies the buffer std::min(n_elems,state_size()) from the buffer pointed to by state_new

This kind of interface provides more flexible state management. Or is there PRNG: s whose state cannot be represented as an array of unsigned integers?

[1] A faster alternative than using threads to maintain the state of the random generator accelerator

+6
source share
1 answer

I wrote a simple test for the approach that I mentioned in the OP comments. This, obviously, has not been tested for battle, but the idea is presented - you must be able to get it from there.

Since the number of bytes read is much less than if you serialized the entire engine, the performance of the two approaches could be comparable. Testing this hypothesis, as well as further optimization, remains as an exercise for the reader.

 #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; } 
0
source

Source: https://habr.com/ru/post/971142/


All Articles