2012-04-17 32 views
19

Tôi tạo một vài nghìn đối tượng trong chương trình của mình dựa trên hàm C++ rand(). Giữ chúng trong bộ nhớ sẽ đầy đủ. Có cách nào để sao chép các hạt giống CURRENT của rand() tại bất kỳ thời điểm nào? Điều này sẽ cho tôi cơ hội để lưu trữ CHỈ hạt giống hiện tại và không phải đối tượng đầy đủ. (do đó tôi có thể tạo lại các đối tượng đó, bằng cách tái tạo cùng một chuỗi con số ngẫu nhiên giống hệt nhau)Cách lấy hạt giống hiện tại từ C++ rand()?

Giải pháp toàn bộ lưu toàn bộ số ngẫu nhiên được đưa ra bởi rand() - không đáng giá. Một khác sẽ là giải pháp là triển khai lớp của riêng tôi cho các số ngẫu nhiên.

Google không cho tôi manh mối tích cực. Có hàng trăm bài báo dạy những điều cơ bản của rand và srand, và tôi không thể tìm thấy những bài cụ thể.

Có ai biết các trình tạo số ngẫu nhiên khác với trình chặn hạt giống được triển khai không?


Cảm ơn bạn đã có câu trả lời nhanh! Có nhiều câu trả lời/giải pháp khả thi hơn cho câu hỏi này, vì vậy tôi đã đưa ra một danh sách các câu trả lời của bạn ở đây.

GIẢI PHÁP:

  1. Câu trả lời ngắn gọn là: không có cách nào tiêu chuẩn để có được những hạt giống

  2. Cách giải quyết gần nhất có thể là để tiết kiệm hạt giống ban đầu trong đầu, và đếm có bao nhiêu lần bạn gọi hàm rand(). Tôi đã đánh dấu giải pháp này là giải pháp vì nó hoạt động trên hàm std :: rand() hiện tại của mọi trình biên dịch (và đây là câu hỏi chính về). Tôi đã chấm điểm CPU 2.0 GHz của mình và thấy rằng tôi có thể gọi & đếm rand() 1.000.000.000 lần trong 35 giây. Điều này nghe có vẻ tốt, nhưng tôi có 80.000 cuộc gọi để tạo ra một đối tượng. Điều này hạn chế số lượng thế hệ đến 50.000 vì kích thước của unsigned dài. Dù sao, đây là mã của tôi:

    class rand2 
    { 
        unsigned long n; 
    
        public: 
    
        rand2() : n(0) {} 
    
        unsigned long rnd() 
        { 
         n++; 
         return rand(); 
        } 
        // get number of rand() calls inside this object 
        unsigned long getno() 
        { 
         return n; 
        } 
        // fast forward to a saved position called rec 
        void fast_forward (unsigned long rec) 
        { 
         while (n < rec) rnd(); 
        } 
    }; 
    
  3. Một cách khác là thực hiện bộ tạo số giả ngẫu nhiên của riêng bạn, giống như một Matteo Italia đề nghị. Đây là giải pháp nhanh nhất và có thể là TỐT NHẤT. Bạn không bị giới hạn ở mức 4,294,967,295 rand() cuộc gọi và không cần phải sử dụng các thư viện khác. Điều đáng nói đến là các trình biên dịch khác nhau có các trình tạo khác nhau. Tôi đã so sánh LCG của Matteo với rand() trong Mingw/GCC 3.4.2 và G ++ 4.3.2. Cả 3 đều khác nhau (với seed = 0).

  4. Sử dụng máy phát từ C++ 11 hoặc các thư viện khác như Cubbi, Jerry Coffin và Mike Seymour đề xuất. Đây là ý tưởng tốt nhất, nếu bạn đã làm việc với họ. Link cho C++ 11 máy phát điện: http://en.cppreference.com/w/cpp/numeric/random (có một số giới thiệu thuật toán ở đây quá)

+0

Nếu trong giải pháp 2 chỉ giới hạn đếm là vấn đề bạn có thể thêm một bộ đếm 'chưa ký dài 'khác để đếm số lần tràn. Điều này sẽ có hiệu quả gấp đôi kích thước bit của truy cập của bạn và có thể, tất nhiên, tiếp tục mở rộng. – bjhend

+0

BTW, thật tuyệt khi bạn thêm một bản tóm tắt giải pháp (truyền thống usenet cũ tốt?). – bjhend

+0

Heh, no. Tôi thực sự đã phải google những gì usenet có nghĩa là :). Chỉ cần nghĩ rằng nó sẽ là tốt đẹp để cung cấp cho một số thông tin phản hồi, rằng các câu trả lời là hữu ích. – user1339629

Trả lời

5

Sử dụng srand() để thiết lập các hạt giống. lưu giá trị bạn đã sử dụng làm hạt giống.

http://cplusplus.com/reference/clibrary/cstdlib/srand/

+5

Tốt nếu bạn muốn tạo lại toàn bộ chuỗi, nhưng không phải nếu bạn muốn trích xuất hạt giống tại bất kỳ thời điểm nào. –

8

Không có cách nào tiêu chuẩn để có được những hạt giống hiện tại (bạn chỉ có thể thiết lập nó qua srand), nhưng bạn có thể reimplement rand() (mà thường là một linear congruential generator) một mình trong một vài dòng mã:

class LCG 
{ 
private: 
    unsigned long next = 1; 
public: 

    LCG(unsigned long seed) : next(seed) {} 

    const unsigned long rand_max = 32767 

    int rand() 
    { 
     next = next * 1103515245 + 12345; 
     return (unsigned int)(next/65536) % 32768; 
    } 

    void reseed(unsigned long seed) 
    { 
     next = seed; 
    } 

    unsigned long getseed() 
    { 
     return next; 
    } 
}; 
13

có ai biết máy phát điện số ngẫu nhiên khác với thực hiện hạt giống-stealer

Tất cả các trình tạo số ngẫu nhiên chuẩn C++ 11 (cũng có sẵn trong TR1 và trong Boost) cung cấp chức năng này. Bạn có thể chỉ cần sao chép các đối tượng máy phát hoặc serialize/deserialize chúng.

5

Các lớp tạo số ngẫu nhiên trong C++ 11 hỗ trợ operator<< để lưu trữ trạng thái của chúng (chủ yếu là hạt giống) và operator>> để đọc lại. Vì vậy, về cơ bản, trước khi bạn tạo đối tượng, lưu trạng thái, sau đó khi bạn cần phải tái tạo cùng một chuỗi, đọc trạng thái trở lại và tắt đi.

4

rand() không cung cấp bất kỳ cách nào để trích xuất hoặc nhân đôi hạt giống. Điều tốt nhất bạn có thể làm là lưu trữ giá trị ban đầu của hạt giống khi bạn đặt nó với srand(), và sau đó xây dựng lại toàn bộ chuỗi từ đó.

Chức năng Posix rand_r() cho phép bạn kiểm soát hạt giống.

Thư viện C++ 11 bao gồm thư viện số ngẫu nhiên dựa trên "công cụ" tạo chuỗi; các công cụ này có thể sao chép và cho phép trạng thái của chúng được trích xuất và khôi phục với các toán tử <<>> để bạn có thể nắm bắt trạng thái của chuỗi bất kỳ lúc nào. Các thư viện rất giống nhau có sẵn trong TR1 và Boost, nếu bạn chưa thể sử dụng C++ 11.

1

Bạn có thể thử lưu giá trị mà bạn đã sử dụng để hạt giống ngay trước (hoặc sau) thương hiệu.

Vì vậy, ví dụ:

int seed = time(NULL); 
srand(time(NULL)); 

cout << seed << endl; 
cout << time(NULL); 

Hai giá trị nên được như vậy.

0

Tôi khuyên bạn nên sử dụng số Mersenne Twister Pseudo-Random Number Generator. Nó nhanh và cung cấp số ngẫu nhiên rất tốt. Bạn có thể gieo rắc các máy phát điện trong các nhà xây dựng của lớp rất đơn giản bằng cách

unsigned long rSeed = 10; 
MTRand myRandGen(rSeed); 

Sau đó, bạn chỉ cần lưu trữ ở đâu đó những hạt giống mà bạn sử dụng để tạo ra các chuỗi ...

0

Có cách nào để sao chép hạt giống CURRENT của rand() tại bất kỳ thời điểm nào?

Sau đây là cách thực hiện cụ thể để lưu và khôi phục trạng thái máy phát số giả ngẫu nhiên (PRNG) hoạt động với thư viện C trên Ubuntu Linux (thử nghiệm trên 14.04 và 16.04).

#include <array> 
#include <cstdlib> 
#include <iostream> 

using namespace std; 

constexpr size_t StateSize = 128; 
using RandState = array<char, StateSize>; 

void save(RandState& state) { 
    RandState tmpState; 
    char* oldState = initstate(1, tmpState.data(), StateSize); 
    copy(oldState, oldState + StateSize, state.data()); 
    setstate(oldState); 
} 

void restore(RandState& state) { 
    setstate(state.data()); 
} 

int main() { 
    cout << "srand(1)\n"; 

    srand(1); 

    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 

    cout << "srand(1)\n"; 

    srand(1); 

    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 

    cout << "save()\n"; 

    RandState state; 
    save(state); 

    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 

    cout << "restore()\n"; 

    restore(state); 

    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
    cout << " rand(): " << rand() << '\n'; 
} 

này dựa trên:

  1. cùng PRNG được sử dụng bởi các thư viện C để lộ cả rand()random() giao diện, và
  2. một số kiến ​​thức về khởi tạo mặc định của PRNG này trong thư mục C thư viện (trạng thái 128 byte).

Nếu chạy, điều này cần đầu ra:

srand(1) 
    rand(): 1804289383 
    rand(): 846930886 
    rand(): 1681692777 
    rand(): 1714636915 
    rand(): 1957747793 
    rand(): 424238335 
    rand(): 719885386 
    rand(): 1649760492 
srand(1) 
    rand(): 1804289383 
    rand(): 846930886 
    rand(): 1681692777 
    rand(): 1714636915 
save() 
    rand(): 1957747793 
    rand(): 424238335 
    rand(): 719885386 
    rand(): 1649760492 
restore() 
    rand(): 1957747793 
    rand(): 424238335 
    rand(): 719885386 
    rand(): 1649760492 

Giải pháp này có thể giúp đỡ trong một số trường hợp (mã mà không thể thay đổi, tái tạo thực hiện cho mục đích gỡ lỗi, vv ...), nhưng nó rõ ràng là không được khuyến cáo như một cái chung (ví dụ: sử dụng C++ 11 PRNG để hỗ trợ điều này).