C++ 中的種子隨機數生成器
C++ 等程式語言不會生成真正的隨機數。相反,計算機使用數學函式生成偽隨機數。
本文討論了生成隨機數中種子的概念以及為 C++ 中用於生成隨機數的函式提供種子的方法。
C++ 中隨機數生成器中種子的概念
C++ 使用確定性演算法生成隨機數序列。因此,數字序列是偽隨機的,而不是純粹的概率。
在這種情況下,種子充當演算法的起點。你不應將其視為生成的第一個數字將是種子。
相反,該演算法從種子定義的分佈中隨機選擇數字。如果你為演算法提供相同的種子,它將生成相同的偽隨機數序列。
但是,大多數情況下,你可能需要為每次執行生成不同的偽隨機數序列。在這種情況下,你需要在每次執行演算法時為其提供不同的種子。
使用 srand()
函式在 C++ 中播種隨機數生成器
srand()
函式接受無符號整數作為引數。它使用引數為生成偽隨機數的演算法播種。
語法:
void srand(unsigned int seed);
如果你提供 1
作為 srand()
函式的引數,它會將偽隨機數生成器初始化為其初始值。生成器產生與上次呼叫 rand()
函式相同的結果。
讓我們看一個示例,該示例使用從使用者那裡獲取的任意數字作為輸入來播種偽隨機數生成器。
示例程式碼:
#include <iostream>
using namespace std;
int main() {
unsigned int seed;
cout << "Enter seed value:\n";
cin >> seed;
srand(seed);
cout << "Successfully seeded the generator\n";
return 0;
}
輸出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
Enter seed value:
12
Successfully seeded the generator
使用 time()
函式在 C++ 中播種隨機數生成器
前一種方法的問題是使用者可以多次輸入相同的數字。如果你需要確保每次執行演算法時都提供不同的種子,請使用 time()
函式為偽隨機數生成器提供種子。
C++ 中的 time()
函式返回當前 UNIX 時間戳,即自 UTC 時間 1970 年 1 月 1 日 00:00 小時以來經過的秒數。
語法:
time_t time(time_t* timer);
該函式將引數作為 time_t
型別的指標。如果你提供對函式的非空引用作為引數,它會將 time_t
型別的物件設定為儲存當前時間戳的引數。
time_t
型別是算術型別的別名,可以儲存當前的 UNIX 時間戳值。它實際上是一個無符號整數值。
示例程式碼:
#include <iostream>
using namespace std;
int main() {
srand(time(NULL));
cout << "Successfully seeded the generator\n";
return 0;
}
請注意,程式碼將 NULL
作為引數傳遞給 time()
函式。根據程式碼,出於任何原因,你不需要型別為 time_t
的物件。
輸出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
Successfully seeded the generator
播種要避免的隨機生成器錯誤
通常,你在程式碼的迴圈中生成一系列偽隨機數。大多數人犯的一個常見錯誤是每次迴圈執行時都為生成器播種。
在這種情況下,你可能會得到重複的數字,因為每次你使用相同的數字播種時,生成器都會建立相同的數字序列。
即使你使用 time()
函式為隨機生成器播種也是如此,因為 time()
函式返回自過去某個固定日期以來經過的秒數。
另一方面,迴圈將執行得更快,因此每次呼叫 time()
函式將保持返回相同的值,直到經過一秒。這樣,你最終將向生成器播種相同的值。
示例程式碼:
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 10; i++) {
srand(time(NULL));
cout << rand() << " ";
}
cout << endl;
return 0;
}
輸出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454
程式碼在迴圈內播種隨機生成器,並且執行十次相同的結果。你應該始終在迴圈之前為隨機數生成器播種以避免這種陷阱。
讓我們看一下程式碼在迴圈外播種隨機生成器的示例。
示例程式碼:
#include <iostream>
using namespace std;
int main() {
srand(time(NULL));
for (int i = 1; i <= 10; i++) {
cout << rand() << " ";
}
cout << endl;
return 0;
}
輸出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
213462937 1076978976 1207347426 8310730 1551061902 266528745 944000672 871831053 1678325834 868781842
觀察程式碼導致不同的數字序列。
まとめ
你可以將種子提供給充當演算法起點的偽隨機生成器,但你應該小心避免陷阱,如文章中所述。否則,可能會導致不需要的結果。
你可以閱讀更多關於在播種後生成偽隨機數序列的 rand()
函式此處。