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





