<random>
定義亂數產生工具,允許建立統一分佈的亂數。
#include <random>
摘要
「亂數產生器」(random number generator) 是一個物件,可產生一連串的虛擬隨機值。 產生統一分佈於指定範圍之值的產生器是「統一亂數產生器」 (Uniform Random Number Generator,URNG)。 設計成當作 URNG 功能的範本類別稱為「引擎」(engine),如果該類別具有特定一般特性的話 (本文稍後會予以討論)。 一般而言,URNG 可以與「分佈」(distribution) 合併使用,方法是將 URNG 做為引數傳遞至分佈的 operator(),以產生由分佈所定義的方式而分佈的值。
這些連結會跳到本文的主要小節:
程式碼範例
分類清單
引擎和分佈
備註
快速提示
以下是使用 <random> 時要記住的一些提示:
在大部分的用途中,URNG 都會產生必須由分佈所圖形化的原始位元 (此情況需要注意的例外狀況是 std::shuffle(),原因是它直接使用 URNG)。
因為執行 URNG 或分佈是修改作業,所以無法安全地同時呼叫 URNG 或分佈的單一具現化。 如需詳細資訊,請參閱C++ 標準程式庫中的執行緒安全。
提供數個引擎的預先定義的 typedef;如果是使用引擎,則這是建立 URNG 的偏好方式。
大部分應用程式的最實用配對是 mt19937 引擎與 uniform_int_distribution (如本文稍後的程式碼範例所示)。
在 <random> 標頭中,有多個選項可供選擇,而且其中任何選項都優於過期的 C Runtime 函式 rand()。 如需 rand() 所發生錯誤以及 <random> 如何解決這些缺點的詳細資訊,請參閱本影片 (英文)。
範例
下列程式碼範例示範如何產生一些亂數,在此情況下,其中有五個使用以不具決定性的種子建立的產生器。
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd; // non-deterministic generator
mt19937 gen(rd()); // to seed mersenne twister.
// replace the call to rd() with a
// constant value to get repeatable
// results.
for (int i = 0; i < 5; ++i) {
cout << gen() << " "; // print the raw output of the generator.
}
cout << endl;
}
輸出:
雖然這些是高品質的亂數,且在每次執行此程式時都會不同,卻不一定在有用的範圍內。 若要控制範圍,請使用平均分佈,如下列程式碼所示:
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd; // non-deterministic generator
mt19937 gen(rd()); // to seed mersenne twister.
uniform_int_distribution<> dist(1,6); // distribute results between 1 and 6 inclusive.
for (int i = 0; i < 5; ++i) {
cout << dist(gen) << " "; // pass the generator to the distribution.
}
cout << endl;
}
輸出:
下一個程式碼範例會使用隨機播放向量和陣列之內容的平均分佈亂數產生器,示範較實際的使用情況。
// cl.exe /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
#include <string>
#include <vector>
#include <functional> // ref()
using namespace std;
template <typename C> void print(const C& c) {
for (const auto& e : c) {
cout << e << " ";
}
cout << endl;
}
template <class URNG>
void test(URNG& urng) {
// Uniform distribution used with a vector
// Distribution is [-5, 5] inclusive
uniform_int_distribution<int> dist(-5, 5);
vector<int> v;
for (int i = 0; i < 20; ++i) {
v.push_back(dist(urng));
}
cout << "Randomized vector: ";
print(v);
// Shuffle an array
// (Notice that shuffle() takes a URNG, not a distribution)
array<string, 26> arr = { { "H", "He", "Li", "Be", "B", "C", "N", "O", "F",
"Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc",
"Ti", "V", "Cr", "Mn", "Fe" } };
shuffle(arr.begin(), arr.end(), urng);
cout << "Randomized array: ";
print(arr);
cout << "--" << endl;
}
int main()
{
// First run: non-seedable, non-deterministic URNG random_device
// Slower but crypto-secure and non-repeatable.
random_device rd;
cout << "Using random_device URNG:" << endl;
test(rd);
// Second run: simple integer seed, repeatable results
cout << "Using constant-seed mersenne twister URNG:" << endl;
mt19937 engine1(12345);
test(engine1);
// Third run: random_device as a seed, different each run
// (Desirable for most purposes)
cout << "Using non-deterministic-seed mersenne twister URNG:" << endl;
mt19937 engine2(rd());
test(engine2);
// Fourth run: "warm-up" sequence as a seed, different each run
// (Advanced uses, allows more than 32 bits of randomness)
cout << "Using non-deterministic-seed \"warm-up\" sequence mersenne twister URNG:" << endl;
array<unsigned int, mt19937::state_size> seed_data;
generate_n(seed_data.begin(), seed_data.size(), ref(rd));
seed_seq seq(begin(seed_data), end(seed_data));
mt19937 engine3(seq);
test(engine3);
}
範例輸出和程式碼備註
此程式碼使用測試範本函式,示範兩個不同的隨機:隨機化整數向量,並隨機播放已編製索引資料的陣列。 測試函式的第一次呼叫會使用具有加密保護,且不具決定性、不可植入、不可重複的 URNG random_device。 第二個測試回合使用 mersenne_twister_engine 做為 URNG,並搭配決定性 32 位元常數種子,這表示結果是可重複的。 第三個測試回合將來自 random_device 的32 位元不具決定性結果植入 mersenne_twister_engine。 第四個測試回合透過使用填入 random_device 結果的種子序列,將此情況延伸,這可以有效地提供比 32 位元更多的不具決定性隨機性 (但還是不具加密保護)。 如需詳細資訊,請繼續閱讀本文。
[回到頁首]
分類清單
統一亂數產生器
通常會根據這些屬性描述 URNG:
期間長度:它使用多少個反覆項目來重複一串產生的數字序列。 愈長愈好。
效能:產生數字的速度,以及使用多少記憶體。 愈小愈好。
品質:產生的序列有多接近真正亂數。 這通常稱為「隨機性」(randomness)。
下列各節列出 <random> 標頭中提供的統一亂數產生器 (URNG)。
[回到頁首]
不具決定性產生器
使用外部裝置,產生不具決定性且以加密編譯方式保護的隨機序列。 通常用於植入引擎。 低效能,品質很高。 如需詳細資訊,請參閱備註。 |
[回到頁首]
具有預先定義參數的引擎 Typedef
用於具現化引擎和引擎配接器。 如需詳細資訊,請參閱引擎和分佈。
名稱 |
描述 |
---|---|
default_random_engine |
預設引擎的類型定義。
|
knuth_b |
Knuth 引擎。
|
minstd_rand0 |
1988 最低標準引擎 (Lewis、Goodman 及 Miller,1969 年)。
|
minstd_rand |
升級的最低標準引擎 minstd_rand0 (Park、Miller 及 Stockmeyer,1993 年)。
|
mt19937 |
32 位元梅森旋轉引擎 (Matsumoto 和 Nishimura,1998 年)。
|
mt19937_64 |
64 位元梅森旋轉引擎 (Matsumoto 和 Nishimura,2000 年)。
|
ranlux24 |
24 位元 RANLUX 引擎 (Martin Lüscher 和 Fred James,1994 年)。
|
ranlux24_base |
用做 ranlux24 的基底。
|
ranlux48 |
48 位元 RANLUX 引擎 (Martin Lüscher 和 Fred James,1994 年)。
|
ranlux48_base |
用做 ranlux48 的基底。
|
[回到頁首]
引擎範本
引擎範本用做獨立 URNG,或用做傳遞給引擎配接器的基底引擎。 這些通常使用預先定義的引擎 typedef 進行具現化,並傳遞給分佈。 如需詳細資訊,請參閱引擎和分佈小節。
使用線性同餘演算法,產生隨機序列。 最為簡單且品質最低。 |
|
使用梅森旋轉演算法,產生隨機序列。 最為複雜,但品質最高 (random_device 類別除外)。 效能極為快速。 |
|
使用帶進位減法演算法,以產生隨機序列。 改善 linear_congruential_engine,但是品質和效能比 mersenne_twister_engine 還要低。 |
[回到頁首]
引擎配接器範本
引擎配接器是可配接其他 (基底) 引擎的範本。 這些通常使用預先定義的引擎 typedef 進行具現化,並傳遞給分佈。 如需詳細資訊,請參閱引擎和分佈小節。
捨棄其基底引擎所傳回的值,以產生隨機序列。 |
|
重新封裝其基底引擎所傳回之值的位元,以產生具有指定位元數的隨機序列。 |
|
透過重新排列基底引擎傳回的值產生隨機序列。 |
[回到本節頂端]
[回到頁首]
亂數分佈
下列各節列出 <random> 標頭中提供的分佈。 分佈是一個後續處理機制,通常使用 URNG 輸出做為輸入,並透過定義的統計可能性密度函式來散發輸出。 如需詳細資訊,請參閱引擎和分佈小節。
[回到頁首]
統一分佈
產生跨封閉間隔 [a, b] (內含 - 內含) 中某個範圍的統一整數值分佈。 |
|
產生跨間隔 [a, b) (內含 - 專有) 中某個範圍的統一實數 (浮點) 值分佈。 |
|
產生跨 [0, 1) (內含 - 排除) 之指定精確度的實數 (浮點) 值平均分佈。 |
[回到本節頂端]
白努利分佈
產生 bool 值的白努利分佈。 |
|
產生整數值的二項式分佈。 |
|
產生整數值的幾何分佈。 |
|
產生整數值的負二項式分佈。 |
[回到本節頂端]
常態分佈
產生實數 (浮點) 值的柯西分佈。 |
|
產生實數 (浮點) 值的卡方分佈。 |
|
產生實數 (浮點) 值的 F 分佈 (也稱為「史耐德柯 F 分佈」或「費雪-史耐德柯分佈」)。 |
|
產生實數 (浮點) 值的對數常態分佈。 |
|
產生實數 (浮點) 值的常態 (高斯) 分佈。 |
|
產生實數 (浮點) 值的學生 t 分佈。 |
[回到本節頂端]
波氏分佈
產生實數 (浮點) 值的指數分佈。 |
|
產生實數 (浮點) 值的極值分佈。 |
|
產生實數 (浮點) 值的 Gamma 分佈。 |
|
產生整數值的波氏分佈。 |
|
產生實數 (浮點) 值的 Weibull 分佈。 |
[回到本節頂端]
取樣分佈
產生離散整數分佈。 |
|
產生實數 (浮點) 值的分段常數分佈。 |
|
產生實數 (浮點) 值的分段線性分佈。 |
[回到本節頂端]
[回到頁首]
公用程式函式
本節列出 <random> 標頭中提供的一般公用程式函式。
產生無偏差干擾種子序列。 用來避免複寫隨機變量資料流。 從引擎具現化許多 URNG 時十分有用。 |
運算子
本節列出 <random> 標頭中提供的運算子。
operator== |
測試運算子左邊的 URNG 是否等於右邊的引擎。 |
operator!= |
測試運算子左邊的 URNG 是否不等於右邊的引擎。 |
operator<< |
將狀態資訊寫入資料流。 |
operator>> |
從資料流中擷取狀態資訊。 |
[回到頁首]
引擎和分佈
如需 <random> 中所定義的每個範本類別分類的詳細資訊,請參閱下列各節。 這兩個範本類別分類採用類型做為引數,並使用共用範本參數名稱,以描述允許做為實際引數類型的類型屬性,如下所示:
IntType 指出 short、int、long、long long、unsigned short、unsigned int、unsigned long 或 unsigned long long。
UIntType 指出 unsigned short、unsigned int、unsigned long 或 unsigned long long。
RealType 指出 float、double 或 long double。
引擎
引擎和引擎配接器是範本,其參數會自訂建立的產生器。
「引擎」(engine) 是一種類別或範本類別,其執行個體 (產生器) 做為統一分佈在最小值與最大值之間的亂數來源。 「引擎配接器」(engine adaptor) 會採用部分其他亂數引擎所產生的值,並將某種類型的演算法套用至那些值,以傳遞一連串具有不同隨機性屬性的值。
每個引擎和引擎配接器都具有下列成員:
typedef numeric-type result_type 是產生器的 operator() 所傳回的類型。 在具現化時,會將 numeric-type 傳遞為範本參數。
result_type operator() 會傳回統一分佈在 min() 與 max() 之間的值。
result_type min() 會傳回產生器的 operator() 所傳回的最小值。 引擎配接器會使用基底引擎的 min() 結果。
result_type max() 會傳回產生器的 operator() 所傳回的最大值。 result_type 是整數 (整數值) 類型時,max() 是可以實際傳回的最大值 (內含);result_type 是浮點 (實數值) 類型時,max() 是大於可傳回之所有值的最小值 (非內含)。 引擎配接器會使用基底引擎的 max() 結果。
void seed(result_type s) 會使用種子值 s 植入產生器。 針對引擎,預設參數支援的簽章是 void seed(result_type s = default_seed) (引擎配接器會定義個別 void seed(),請參閱下一個小節)。
template <class Seq> void seed(Seq& q) 會使用 seed_seq Seq 植入產生器。
具有引數 result_type x 的明確建構函式,會建立植入的產生器,就像藉由呼叫 seed(x) 一樣。
具有引數 seed_seq& seq 的明確建構函式,會建立植入的產生器,就像藉由呼叫 seed(seq) 一樣。
void discard(unsigned long long count) 會有效率地呼叫 operator() count 次,並捨棄每個值。
引擎配接器額外支援這些成員 (Engine 是引擎配接器的第一個範本參數,會指定基底引擎類型):
初始化產生器的預設建構函式,就像來自基底引擎的預設建構函式一樣。
引數為 const Engine& eng 的明確建構函式。 這是使用基底引擎來支援複製建構函式。
引數為 Engine&& eng 的明確建構函式。 這是使用基底引擎來支援移動建構函式。
使用基底引擎預設種子值來初始化產生器的 void seed()。
傳回用於建構產生器之基底引擎的 const Engine& base() 屬性函式。
每個引擎都會維護一個「狀態」(state),而狀態會決定後續 operator() 呼叫所產生的值序列。 使用 operator== 和 operator!=,可以比較從相同類型的引擎具現化的兩個產生器的狀態。 如果兩種狀態比較為相等,則會產生相同的值序列。 使用產生器的 operator<<,可以將物件的狀態儲存至資料流,且形式為一連串 32 位元不帶正負號值。 狀態在儲存之後並不會變更。 使用 operator>>,可以將儲存的狀態讀取至從相同類型的引擎具現化的產生器。
[回到頁首]
分佈
分佈是一種類別或範本類別,其執行個體會將取自引擎之統一分佈亂數的資料流,轉換為具有特定分佈之亂數的資料流。 每個分佈都有下列成員:
typedef numeric-type result_type 是分佈的 operator() 所傳回的類型。 在具現化時,會將 numeric-type 傳遞為範本參數。
template <class URNG> result_type operator()(URNG& gen) 會傳回根據分佈定義所分佈的值,方法是使用 gen 做為統一分佈隨機值的來源,以及儲存之「分佈的參數」(parameters of the distribution)。
template <class URNG> result_type operator()(URNG& gen, param_type p) 會傳回按照分佈定義所分佈的值,方法是使用 gen 做為統一分佈隨機值的來源,以及參數結構 p。
typedef unspecified-type param_type 是選擇性地傳遞至 operator() 的參數封裝,以及用來取代儲存的參數以產生其傳回值。
const param& 建構函式會從其引數初始化儲存的參數。
param_type param() const 取得儲存的參數。
void param(const param_type&) 會從其引數設定儲存的參數。
result_type min() 會傳回分佈的 operator() 所傳回的最小值。
result_type max() 會傳回分佈的 operator() 所傳回的最大值。 result_type 是整數 (整數值) 類型時,max() 是可以實際傳回的最大值 (內含);result_type 是浮點 (實數值) 類型時,max() 是大於可傳回之所有值的最小值 (非內含)。
void reset() 會捨棄任何快取的值,讓下個 operator() 呼叫的結果不是取決於呼叫之前取自引擎的任何值。
參數結構是一個物件,其中儲存分佈所需的所有參數。 它包含:
typedef distribution-type distribution_type,這是其分佈的類型。
一個或多個建構函式,採用與分佈建構函式所採用的相同參數清單。
與分佈相同的參數存取函式。
等號和不等比較運算子。
如需詳細資訊,請參閱本文之前的連結中,本主題下面的參考副主題。
[回到頁首]
備註
在 Visual Studio 中,有兩個極為有用的 URNG (mt19937 和 random_device),如此比較表所示:
URNG |
快速? |
具有加密保護? |
可植入? |
決定性? |
---|---|---|---|---|
mt19937 |
是 |
否 |
是 |
是* |
random_device |
否 |
是 |
否 |
否 |
* 提供已知種子時。
雖然 ISO C++ 標準不需要加密保護 random_device,但在 Visual Studio 中,會將它實作為進行加密保護。 「加密保護」這個詞彙並不表示提供保證,而是指給定隨機演算法所提供的最低熵層級 (因此是預測層級)。 如需詳細資訊,請參閱 Wikipedia 文章:加密保護虛擬亂數產生器 (英文)。因為 ISO C++ 標準不需要這個,所以其他平台可能會將 random_device 實作為簡單虛擬亂數產生器 (未加密保護),而且可能只適合做為另一個產生器的種子來源。 在跨平台程式碼中使用 random_device 時,請參閱哪些平台的文件。
透過定義,random_device 結果是無法重新產生的,而且副作用是執行速度會明顯比其他 URNG 慢。 雖然您可能想要使用 random_device 呼叫來進行植入 (如程式碼範例中所示),但大部分不需要加密保護的應用程式都會使用 mt19937 或類似的引擎。
[回到頁首]