隨機數(shù)的種子問題
掃描二維碼
隨時隨地手機看文章
開發(fā)過程中,經(jīng)常會遇到生成隨機數(shù)的需求,本文會詳細介紹C++中生成隨機數(shù)的方法以及一些注意事項。
隨機數(shù)核心組件
C++11引入了庫,這個庫提供了隨機數(shù)生成工具。
下面是C++中生成隨機數(shù)的核心組件:
- 隨機數(shù)引擎:生成偽隨機數(shù)的算法。
- 隨機數(shù)分布:將隨機數(shù)引擎生成的數(shù)映射到特定范圍或分布。
- 真隨機數(shù)生成器:如std::random_device,用于生成高質(zhì)量的隨機數(shù)。
種子(Seed)
- 種子是隨機數(shù)生成器的初始值。
- 相同的種子會生成相同的隨機數(shù)序列。
- 常用種子來源:
- 當前時間:std::time(0)。
- 真隨機數(shù)生成器:std::random_device。
偽隨機數(shù)引擎
- std::mt19937:基于Mersenne Twister算法,周期長,隨機性好。
- std::minstd_rand:線性同余生成器,速度快,但隨機性較差。
- std::default_random_engine:默認引擎,實現(xiàn)可能因平臺而異。
隨機數(shù)分布
- 均勻分布:std::uniform_int_distribution、std::uniform_real_distribution。
- 正態(tài)分布:std::normal_distribution。
- 伯努利分布:std::bernoulli_distribution。
真隨機數(shù)生成器
- std::random_device:依賴于硬件或操作系統(tǒng)提供的隨機數(shù)源。
- 適用于生成種子或高安全性場景。
生成隨機數(shù)的方法
std::rand
std::rand是C標準庫中的隨機數(shù)生成函數(shù),C++中仍然可以使用,但它的隨機性較差,且范圍固定。
#include #include // for std::rand and std::srand #include // for std::time int main() { // 使用當前時間作為種子 std::srand(std::time(0)); // 生成一個隨機數(shù) int random_value = std::rand(); std::cout << "Random value: " << random_value << std::endl; // 生成一個范圍在 [0, 99] 的隨機數(shù) int random_in_range = std::rand() % 100; std::cout << "Random value in [0, 99]: " << random_in_range << std::endl; return 0; }
缺點:
- std::rand生成的隨機數(shù)質(zhì)量較低。
- 范圍限制需要手動調(diào)整(如%操作符)。
- 種子設(shè)置不夠靈活。
std::random_device真隨機數(shù)
std::random_device是一個真隨機數(shù)生成器,通常用于生成高質(zhì)量的隨機數(shù)種子。
示例代碼:
#include #include int main() { std::random_device rd; // 真隨機數(shù)生成器 std::cout << "Random value: " << rd() << std::endl; return 0; }
注意:
- 在某些平臺上,std::random_device可能會退化為偽隨機數(shù)生成器。
- 大量每次生成隨機數(shù)都使用std::random_device,性能較差。
- 通常用于生成種子,而不是直接用于生成大量隨機數(shù)。
偽隨機數(shù)引擎和分布
C++11引入了多種偽隨機數(shù)引擎和分布,可以生成高質(zhì)量的隨機數(shù)。
常用隨機數(shù)引擎:
- std::default_random_engine:默認的隨機數(shù)引擎。
- std::mt19937:Mersenne Twister算法,高質(zhì)量隨機數(shù)引擎。
- std::minstd_rand:線性同余生成器。
常用隨機數(shù)分布:
- std::uniform_int_distribution:均勻分布的整數(shù)。
- std::uniform_real_distribution:均勻分布的浮點數(shù)。
- std::normal_distribution:正態(tài)分布的浮點數(shù)。
- std::bernoulli_distribution:伯努利分布(布爾值)。
示例代碼:
#include #include int main() { // 使用 Mersenne Twister 引擎 std::mt19937 rng(std::random_device{}()); // 均勻分布的整數(shù) [1, 100] std::uniform_int_distribution<int> dist(1, 100); int random_int = dist(rng); std::cout << "Random integer in [1, 100]: " << random_int << std::endl; // 均勻分布的浮點數(shù) [0.0, 1.0) std::uniform_real_distribution<double> dist_double(0.0, 1.0); double random_double = dist_double(rng); std::cout << "Random double in [0.0, 1.0): " << random_double << std::endl; // 正態(tài)分布的浮點數(shù)(均值 0.0,標準差 1.0) std::normal_distribution<double> dist_normal(0.0, 1.0); double random_normal = dist_normal(rng); std::cout << "Random normal value: " << random_normal << std::endl; return 0; }
優(yōu)點:
- 隨機數(shù)質(zhì)量高。
- 分布靈活,支持多種分布類型。
生成隨機字符串
可以使用隨機數(shù)生成器生成隨機字符串。
示例代碼:
#include #include #include std::string generate_random_string(size_t length) { const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; std::mt19937 rng(std::random_device{}()); std::uniform_int_distribution<size_t> dist(0, charset.size() - 1); std::string result; for (size_t i = 0; i < length; ++i) { result += charset[dist(rng)]; } return result; } int main() { std::string random_str = generate_random_string(10); std::cout << "Random string: " << random_str << std::endl; return 0; }
注意事項
std::mt19937 e{std::random_device{}()};
直接這樣寫有什么問題?
如果你只需要生成一次隨機數(shù),這樣寫沒問題。但如果你需要多次生成隨機數(shù),最好避免反復(fù)創(chuàng)建和銷毀std::random_device對象,因為這會帶來不必要的開銷。
原因:
- 每次創(chuàng)建std::random_device對象時,都會初始化一個新的文件句柄(在類Unix系統(tǒng)上,它通常是對/dev/urandom的封裝),這會帶來文件系統(tǒng)操作的開銷。(不同操作系統(tǒng)的實現(xiàn)不同)
- 每次從std::random_device讀取隨機數(shù)時,都會觸發(fā)系統(tǒng)調(diào)用(如read系統(tǒng)調(diào)用),這可能會影響性能。
- 在Windows系統(tǒng)上,std::random_device通常是對微軟加密API的封裝,每次創(chuàng)建和銷毀std::random_device對象時,都會初始化和銷毀加密庫的接口,這也會帶來額外的開銷。
因此,如果需要頻繁生成隨機數(shù),這種寫法可能會導致性能問題。當然,如果你的應(yīng)用程序?qū)π阅芤蟛桓?,這種寫法也是可以接受的。
常見的寫法:
在許多示例、網(wǎng)站和文章中,通常會看到以下寫法:
std::random_device rd; std::mt19937 e{rd()}; // 或者 std::default_random_engine e{rd()}; std::uniform_int_distribution<int> dist{1, 5};
這種寫法的優(yōu)點是:
- std::random_device只被創(chuàng)建一次,避免了反復(fù)初始化和銷毀的開銷。
- std::mt19937是一個偽隨機數(shù)生成器,它的初始化只需要一個種子(由std::random_device提供),之后的所有隨機數(shù)生成都在用戶進程中完成,不會涉及系統(tǒng)調(diào)用。
std::mt19937vsstd::random_device
std::mt19937
- 偽隨機數(shù)生成器,基于Mersenne Twister算法。
- 它是自包含的,完全在用戶進程中運行,不會調(diào)用操作系統(tǒng)或其他外部資源。
- 代碼非常穩(wěn)定,跨平臺性能一致,在任何平臺上編譯和運行它,都會得到相似的性能和結(jié)果。
std::random_device
- 是一個真隨機數(shù)生成器,但其實現(xiàn)是不透明的,我們無法確切知道它的底層實現(xiàn)是什么,它會做什么,或者它的效率如何,不同系統(tǒng)實現(xiàn)不一定相同。
- 每次從std::random_device讀取隨機數(shù)時,可能會觸發(fā)系統(tǒng)調(diào)用,因此其性能(每字節(jié)的周期數(shù))可能遠低于std::mt19937。
- 它通常用于生成種子,而不是直接生成大量隨機數(shù)。
- 如果你為某些嵌入式設(shè)備或手機進行交叉編譯,它的行為可能更加不可預(yù)測。
總結(jié)
- 對于簡單的隨機數(shù)需求,可以使用std::rand。
- 對于高質(zhì)量的隨機數(shù),推薦使用std::mt19937引擎和分布,這是一個高效且可靠的偽隨機數(shù)生成器,適合在用戶進程中生成大量隨機數(shù)。
- 對于高安全性場景,可以使用std::random_device生成真隨機數(shù)。
- std::random_device的行為因平臺而異,通常用于生成種子,而不是直接生成大量隨機數(shù)。
- 如果需要頻繁生成隨機數(shù),建議避免反復(fù)創(chuàng)建和銷毀std::random_device對象,而是將其作為種子生成器,只初始化一次。





