2009-06-10 21 views
5

Tôi đã nhìn thấy một vài khuyến nghị không cho việc tạo các trình tạo số giả ngẫu nhiên nhiều lần một lần, nhưng không bao giờ được giải thích kỹ lưỡng. Tất nhiên, nó rất dễ dàng để xem lý do tại sao sau (C/C++) ví dụ không phải là một ý tưởng tốt:Các vấn đề với việc tạo một trình tạo số giả ngẫu nhiên nhiều lần?

int get_rand() { 
    srand(time(NULL)); 
    return rand(); 
} 

kể từ khi gọi get_rand vài lần mỗi giây tạo ra kết quả lặp lại.

Nhưng không phải ví dụ sau đây vẫn là giải pháp có thể chấp nhận được?

MyRand.h

#ifndef MY_RAND_H 
#define MY_RAND_H 

class MyRand 
{ 
    public: 
    MyRand(); 
    int get_rand() const; 
    private: 
    static unsigned int seed_base; 
}; 

#endif 

MyRand.cpp

#include <ctime> 
#include <cstdlib> 
#include "MyRand.h" 

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL)); 

MyRand::MyRand() 
{ 
    srand(seed_base++); 
} 

int MyRand::get_rand() const 
{ 
    return rand(); 
} 

main.cpp

#include <iostream> 
#include "MyRand.h" 

int main(int argc, char *argv[]) 
{ 
    for (int i = 0; i < 100; i++) 
    { 
    MyRand r; 
    std::cout << r.get_rand() << " "; 
    } 
} 

tức là mặc dù MyRand: hàm tạo của s được gọi nhiều lần liên tiếp nhanh, mỗi cuộc gọi đến srand có thông số khác. Rõ ràng, đây không phải là chủ đề an toàn, nhưng sau đó lại không phải là rand.

+0

Tôi có thể thêm rằng toàn bộ mục đích của bài tập này là để giảm "gánh nặng" khi gọi srand từ khách hàng của 'MyRand', trong đó' MyRand' có thể là người mẫu. Nhưng mặt khác, nếu chúng ta cũng chế tạo bánh xe tài sản, đồng xu ném vv trong cùng một cách, chúng ta sẽ nhận được rất nhiều hạt giống. – a038c56f

Trả lời

6

Mỗi lần bạn gọi hàm giả lập số ngẫu nhiên, trình tạo sẽ mất một số trạng thái bên trong và tạo ra một số giả ngẫu nhiên và trạng thái nội bộ mới. Thuật toán để chuyển đổi trạng thái bên trong được lựa chọn cẩn thận để đầu ra xuất hiện ngẫu nhiên.

Khi bạn gieo trình tạo số ngẫu nhiên, về cơ bản bạn sẽ đặt trạng thái nội bộ này. Nếu bạn đặt lại trạng thái nội bộ thành một số giá trị dự đoán được, bạn sẽ mất sự xuất hiện của tính ngẫu nhiên.

Ví dụ: RNG phổ biến, đơn giản là trình tạo đồng tuyến tuyến tính. Các số được tạo như sau:

X[n+1] = (a X[n] + c) mod m 

Trong trường hợp này, X [n + 1] vừa là kết quả vừa là trạng thái nội bộ mới.Nếu bạn giống máy phát điện mỗi lần như bạn đề nghị ở trên, bạn sẽ nhận được một chuỗi trông như thế này:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...} 

nơi b là seed_base của bạn. Điều này không có vẻ ngẫu nhiên chút nào.

+0

Ví dụ main.cpp của tôi hơi phóng đại với mục đích chứng tỏ sự thiếu sót của lỗ hổng của ví dụ đầu tiên. Việc khởi tạo một đối tượng mới cho mỗi lệnh gọi 'get_rand' sẽ dẫn đến tình huống bạn mô tả ở trên, nhưng đó chỉ là lập trình lãng phí. Giả sử rằng số lượng 'MyRand' instantiations ít so với' get_rand' call, mọi thứ trông tốt hơn một chút. – a038c56f

1

Nếu hạt giống của bạn có thể dự đoán được, nó ở đây vì bạn chỉ cần tăng nó, đầu ra từ rand() cũng có thể dự đoán được.

Điều đó thực sự phụ thuộc vào lý do bạn muốn tạo số ngẫu nhiên và cách "ngẫu nhiên" là ngẫu nhiên có thể chấp nhận được cho bạn. Trong ví dụ của bạn, nó có thể tránh trùng lặp liên tiếp nhanh chóng và điều đó có thể đủ tốt cho bạn. Sau khi tất cả, những gì quan trọng là nó chạy.

Trên hầu hết mọi nền tảng, có một cách tốt hơn để tạo số ngẫu nhiên hơn rand().

1

Đây là quá trình xử lý bổ sung mà không cần phải thực hiện.

Trong trường hợp đó, tôi chỉ gọi hàm tạo một lần với một hạt giống dựa trên thời gian trước khi bắt đầu vòng lặp. Điều đó sẽ đảm bảo các kết quả ngẫu nhiên mà không phải trả thêm chi phí cho việc thay đổi hạt giống cho mỗi lần lặp lại.

Tôi sẽ không nghĩ phương pháp của bạn là bất kỳ khác ngẫu nhiên hơn thế.

0

Bạn có thể nghĩ về việc tạo số ngẫu nhiên (điều này không thực sự đúng với việc thực hiện khôn ngoan nữa, nhưng đóng vai trò như một minh họa) như một bảng giá trị. Nếu bạn nhớ làm bất kỳ công cụ nào trong số liệu thống kê để thực hiện các mẫu ngẫu nhiên đơn giản, một hạt giống về cơ bản sẽ cho bạn biết hàng và cột nào bắt đầu tại bảng lớn các số ngẫu nhiên của bạn. Việc lặp lại lặp đi lặp lại chỉ đơn giản là không cần thiết vì chúng ta đã có thể giả định rằng các số thường được phân phối rồi.

Chỉ đơn giản là không có lợi ích bổ sung cho việc gieo hạt nhiều lần vì điều này phải đủ tốt (tùy thuộc vào đơn đăng ký). Nếu bạn cần số ngẫu nhiên "nhiều hơn", có nhiều phương pháp tạo số ngẫu nhiên. Một trường hợp mà tôi có thể nghĩ đến là tạo ra các số ngẫu nhiên theo cách an toàn.

Khi giải pháp của bạn được chấp nhận, số của bạn sẽ không ngẫu nhiên hơn so với gieo hạt một lần, trên toàn cầu. srand thường không nên thuộc về một nhà xây dựng. Nếu bạn muốn hỗ trợ số ngẫu nhiên, hạt giống một lần khi chương trình bắt đầu, và quên nó đi.