Câu hỏi quá rộng để có một câu trả lời đầy đủ, nhưng hãy để tôi chọn ra một vài điểm thú vị:
Tại sao "có khả năng như nhau"
Giả sử bạn có một trình tạo số ngẫu nhiên đơn giản tạo ra các số 0, 1, ..., 10 mỗi số với xác suất bằng nhau (hãy nghĩ về điều này là cổ điển rand()
). Bây giờ bạn muốn một số ngẫu nhiên trong phạm vi 0, 1, 2, mỗi số có xác suất bằng nhau. Phản ứng giật đầu gối của bạn sẽ xảy ra rand() % 3
. Nhưng chờ đợi, phần còn lại 0 và 1 xảy ra thường xuyên hơn phần còn lại 2, vì vậy điều này không chính xác!
Đây là lý do tại sao chúng ta cần các phân phối thích hợp , lấy nguồn các số nguyên ngẫu nhiên đồng nhất và biến chúng thành phân phối mong muốn của chúng ta, như Uniform[0,2]
trong ví dụ. Tốt nhất hãy để nó vào một thư viện tốt!
Động cơ
Do đó, trung tâm của tất cả sự ngẫu nhiên là một trình tạo số giả ngẫu nhiên tốt tạo ra một chuỗi các số phân bố đồng đều trong một khoảng thời gian nhất định và lý tưởng là có một khoảng thời gian rất dài. Việc triển khai tiêu chuẩn rand()
thường không phải là tốt nhất, và do đó, thật tốt khi có sự lựa chọn. Linear-congruential và Mersenne twister là hai lựa chọn tốt (LG thực tế cũng thường được sử dụng rand()
); một lần nữa, thật tốt khi để thư viện xử lý việc đó.
Làm thế nào nó hoạt động
Dễ dàng: đầu tiên, thiết lập một động cơ và gieo nó. Hạt giống hoàn toàn xác định toàn bộ chuỗi số "ngẫu nhiên", vì vậy a) sử dụng một số khác (ví dụ lấy từ /dev/urandom
) mỗi lần và b) lưu trữ hạt giống nếu bạn muốn tạo lại một chuỗi các lựa chọn ngẫu nhiên.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Bây giờ chúng ta có thể tạo các bản phân phối:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... Và sử dụng công cụ để tạo ra các số ngẫu nhiên!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Đồng tiền
Một lý do quan trọng hơn để thích <random>
hơn so với truyền thống rand()
là giờ đây nó đã rất rõ ràng và rõ ràng về cách tạo an toàn cho luồng tạo số ngẫu nhiên: Cung cấp cho mỗi luồng một công cụ cục bộ luồng của riêng nó, được gieo trên hạt cục bộ của luồng hoặc đồng bộ hóa quyền truy cập đến đối tượng động cơ.
Misc
- Một bài báo thú vị về TR1 ngẫu nhiên trên codeguru.
- Wikipedia có một bản tóm tắt tốt (cảm ơn, @Justin).
- Về nguyên tắc, mỗi động cơ phải
result_type
gõ a , là loại tích phân chính xác để sử dụng cho hạt giống. Tôi nghĩ rằng tôi đã có một thi buggy lần mà buộc tôi phải buộc các hạt giống cho std::mt19937
đến uint32_t
trên x64, cuối cùng này cần được cố định và bạn có thể nói MyRNG::result_type seed_val
và do đó làm cho động cơ rất dễ dàng thay thế.
rand
, bạn nên xem nhanh wikipedia để biết một số khái niệm cơ bản về thống kê và RNG, nếu không sẽ rất khó để giải thích cho bạn cơ sở lý luận<random>
và cách sử dụng các phần khác nhau của nó.