2014-06-21 16 views
6

Cách tốt nhất để lưu trữ trạng thái của trình tạo ngẫu nhiên C++ 11 mà không sử dụng giao diện iostream là gì. Tôi muốn làm như thay thế đầu tiên được liệt kê ở đây [1]? Tuy nhiên, cách tiếp cận này yêu cầu đối tượng chứa trạng thái PRNG và chỉ trạng thái PRNG. Trong một phần, nó thất bại nếu việc triển khai sử dụng mẫu pimpl (ít nhất điều này có khả năng làm hỏng ứng dụng khi tải lại trạng thái thay vì tải nó với dữ liệu xấu), hoặc có nhiều biến trạng thái kết hợp với đối tượng PRNG không có để thực hiện với chuỗi được tạo.Lưu trạng thái của C++ 11 trình tạo ngẫu nhiên mà không cần sử dụng iostream

Kích thước của đối tượng thực hiện định nghĩa:

  • g++ (tdm64-1) 4.7.1 cho sizeof(std::mt19937)==2504 nhưng
  • Ideonehttp://ideone.com/41vY5j cho chức năng thành viên 2500

tôi đang thiếu như

  1. size_t state_size();
  2. const size_t* get_state() const;
  3. void set_state(size_t n_elems,const size_t* state_new);

(1) sẽ trở lại kích thước của mảng trạng thái máy phát ngẫu nhiên

(2) sẽ trả về một con trỏ đến mảng nhà nước. Con trỏ được quản lý bởi PRNG.

(3) sẽ sao chép các bộ đệm std::min(n_elems,state_size()) từ bộ đệm được trỏ đến bởi state_new

Đây là loại giao diện cho phép thao tác nhà nước linh hoạt hơn. Hoặc là có bất kỳ PRNG: s có nhà nước không thể được đại diện như là một mảng của số nguyên unsigned?

[1] Faster alternative than using streams to save boost random generator state

+0

Bạn có thể tìm thấy [câu hỏi này] (http://stackoverflow.com/questions/11563963/writing-a-binary-file-in-c-very-fast) hữu ích. Khác hơn thế, tôi không nghĩ rằng nó có thể serialize một RNG (hoặc bất kỳ đối tượng, thực sự) mà không có một số kiến ​​thức về việc thực hiện cơ bản. Nếu nó có thể xảy ra, nó có lẽ sẽ liên quan đến một số ... hackery kinky. –

+0

@MoreAxes Câu hỏi được đề cập không liên quan. Ngoài ra, đây không phải là vấn đề về hiệu suất, mà là vấn đề tương thích giao diện: giao diện I/O không xuất phát từ bất kỳ lớp iostream nào và tôi không thể sử dụng các phương thức đã cung cấp mà không sao chép trước đó vào chuỗi, sau đó chuyển đổi thành nhị phân và cuối cùng viết nó bằng chức năng ChunkIO :: Writer :: dataWrite. – user877329

+1

'g ++' 's và Ideone 'sizeof std :: mt19937' trả về giá trị tương ứng 8 và 4 byte lớn hơn những gì cần thiết để lưu trữ mảng trạng thái của Mersenne Twister.Nếu đây là một giá trị duy nhất trong cả hai trường hợp, thì tôi đặt cược đó là một con trỏ (tôi giả sử bạn đang sử dụng hệ thống 64 bit và Ideone), mà bạn cần phải xử lý phù hợp trong quá trình tuần tự hóa. Nếu nó là một giá trị không phải con trỏ (hoặc hai trong số chúng trong trường hợp 'g ++'), thì có thể an toàn để tuần tự hóa nó như là. –

Trả lời

0

Tôi đã viết một (-ish) thử nghiệm đơn giản cho cách tiếp cận tôi đã đề cập trong các ý kiến ​​của OP. Nó rõ ràng không phải là thử nghiệm chiến đấu, nhưng ý tưởng được thể hiện - bạn sẽ có thể lấy nó từ đây.

Vì số lượng byte đọc nhỏ hơn rất nhiều so với nếu một byte sắp xếp toàn bộ động cơ, hiệu suất của hai phương pháp này thực sự có thể so sánh được. Thử nghiệm giả thuyết này, cũng như tối ưu hóa thêm, được để lại như một bài tập cho người đọc.

#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; 
} 
Các vấn đề liên quan