C++ 隨機函式庫將確定性的引擎與分布分開。引擎會在指定輸出範圍內產生均勻分布值的序列。分布會消耗引擎輸出,並將它映射至所要求的統計分布。當狀態必須延續時,請讓兩個物件都保持存活。
#include <random>
std::mt19937_64 engine{std::random_device{}()};
std::uniform_int_distribution<int> die{1, 6};
int roll = die(engine); // Both engine and distribution may advance state.
std::mt19937 與 std::mt19937_64 是偽隨機引擎:固定的初始狀態會產生可重現的序列。std::default_random_engine 由實作定義,因此當測試、檔案或協定要求不同實作之間使用相同序列時,它並不適用。
依需求設定種子 #
確定性測試應使用明確且有文件記載的種子。當產生的資料必須重現時,請在測試失敗時記錄種子。
std::mt19937 engine{0x5EEDu};當實作具有合適來源時,std::random_device 旨在提供非確定性的隨機數,但實作可能提供確定性後備方案。其 entropy() 值是估計值;沒有估計值時可能為零。因此,random_device 適合作為非密碼編譯模擬的盡力式種子來源,而不是可攜式的密碼安全保證。對金鑰、token、salt 與 nonce,請使用作業系統的密碼編譯隨機 API 或經審查的密碼編譯函式庫。
std::seed_seq 可以將數個來源字組擴展至引擎狀態。需要獨立串流時,避免以相同的小整數為數個長期存在的引擎設定種子;請改為建立並記錄串流配置原則。
分布產生有界值而不引入取模偏差 #
請使用能表達所需結果的分布:
std::uniform_int_distribution<int> index{0, static_cast<int>(size - 1)};
std::bernoulli_distribution enabled{0.25};
std::normal_distribution<double> noise{0.0, 1.0};uniform_int_distribution 的界限包含端點。建構或設定參數前,請驗證動態界限:a > b 的範圍違反分布的前置條件。不要使用 engine() % n 取得有界均勻結果,除非引擎的輸出範圍可整除 n;否則某些餘數出現的頻率會較高。標準分布會為其指定分布執行必要的拒絕或映射工作。
分布不能與引擎互換。分布可以保留狀態,例如常態分布的實作可能快取產生的值,因此請根據模擬的可重現性需求保留或重設分布狀態。
生命週期、可重現性與序列化 #
以值傳遞引擎會刻意建立分開的序列狀態。當輔助程式必須推進呼叫者的序列時,請以參照傳遞。
int roll_die(std::mt19937_64& engine) {
static std::uniform_int_distribution<int> die{1, 6};
return die(engine);
}只有在呼叫已同步時,這裡的 static 分布才安全;它可能具有可變狀態。當每個模擬的參數或狀態不同時,最好明確管理分布所有權。
標準引擎支援以格式化串流輸入與輸出狀態,因此能在相同標準引擎規格內支援檢查點。請將引擎型別、種子原則與分布參數和序列化狀態一同保存。序列化序列不是密碼秘密儲存區,專案也不應宣稱超出所選引擎與實作環境保證的跨版本可重現性。
並行 #
引擎與分布物件是可變狀態,未同步時不得並行修改。當串流彼此獨立時,為每個執行緒提供自己的引擎與分布;若需要單一有序序列,則使用互斥鎖保護共用產生器。執行緒區域產生器需要刻意的種子原則;將相同種子直接複製到每個執行緒會建立相關且相同的串流,而不是平行隨機性。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)