2009-06-10 124 views
5

我見過很多關於每次執行不超過一次僞隨機數生成器的建議,但從來沒有伴隨徹底的解釋。當然,很容易看出爲什麼以下(C/C++)的例子是不是一個好主意:多次播種僞隨機數生成器的問題?

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

因爲每秒調用get_rand多次重複產生的結果。

但是下面的例子仍然不是一個可以接受的解決方案嗎?

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() << " "; 
    } 
} 

即即使MyRand:s的構造函數被連續多次調用,每次調用srand都有不同的參數。很明顯,這不是線程安全的,但是再也不是rand

+0

我想補充一點這個練習的整個目的是緩解由`MyRand`,其中`MyRand`可能被造型模具的客戶端調用函數srand的「包袱」。但另一方面,如果我們也以相同的方式製造幸運輪,拋硬幣等,我們將獲得很多種子。 – a038c56f 2009-06-10 18:42:06

回答

6

每次調用僞隨機數生成器函數時,生成器都會採用一些內部狀態並生成一個僞隨機數和一個新的內部狀態。仔細選擇用於轉換內部狀態的算法,以使輸出看起來是隨機的。

當你種下隨機數發生器時,你基本上就是設置這個內部狀態。如果您將內部狀態重置爲某個可預測的值,您將失去隨機性。例如,一個流行的,簡單的RNG是線性同餘發生器。數字生成像這樣:

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

在這種情況下,X [n + 1]既是結果又是新的內部狀態。如果你每次播種發生器你上面建議,你會得到一個序列,看起來像這樣:

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

其中B是你的seed_base。這看起來並不隨意。

+0

我的main.cpp例子有點誇張,目的是證明缺少第一個例子的缺陷。爲每個調用get_rand實例化一個新對象將導致上面描述的情況,但這只是浪費編程。假設與`get_rand`調用相比,`MyRand`實例化的數量很少,事情看起來好一點。 – a038c56f 2009-06-10 18:31:40

1

如果你的種子是可預測的,它在這裏,因爲你只是遞增它,rand()的輸出也是可以預測的。

這真的取決於你爲什麼要產生隨機數,以及「隨機」對你來說是多麼可接受的隨機數。在你的例子中,它可能會避免連續的重複,這對你來說可能是足夠好的。畢竟,重要的是它的運行。

在幾乎每個平臺上,都有一個比rand()更好的生成隨機數的方法。

1

那麼它是額外的處理,不需要做。

在這種情況下,我只是在循環開始之前用基於時間的種子調用構造函數一次。這將保證隨機結果沒有每次迭代改變種子的額外開銷。

我不會認爲你的方法是任何更多隨機比。

0

您可以將隨機數生成(這不是嚴格意義上的實現方式,但作爲一個例子)作爲值表。如果你記得在統計學中做這些簡單的隨機樣本,種子基本上會告訴你在你的大表中隨機數字開始的行和列。因爲我們已經可以假設這些數字已經正常分佈了,所以一遍又一遍地重新調整是不必要的。

由於這應該足夠好(取決於應用程序),所以根本沒有增加種子播種的好處。如果你確實需要「更多」的隨機數,有很多隨機數生成的方法。我能想到的一種情況是以線程安全的方式生成隨機數。

雖然您的解決方案是可以接受的,但您的數字不會比在全球範圍內播種一次更隨機。 srand通常不應該屬於構造函數。如果您想支持隨機數字,請在程序啓動時種一次,然後忘記它。