引言
隨機數在模擬、遊戲、加密、測試等領域不可或缺。C/C++ 提供了 rand()/srand() 這對經典函數,但其隨機品質與易用性在現代標準下已顯不足。本文系統梳理傳統用法的陷阱,並介紹 C++11 <random> 標準庫的現代解決方案。
傳統 rand()/srand() 機制與陷阱
基本運作原理
rand() 返回 0 到 RAND_MAX(至少 32767)之間的偽隨機整數。
- 預設種子為 1,導致每次程式啟動產生相同序列——利於除錯,不利於實際應用。
srand(seed) 設定種子;相同種子產生相同序列。
常見錯誤與修正
| 錯誤寫法 | 問題 | 正確做法 |
重複呼叫 srand(time(NULL)) | 若在迴圈內呼叫,同一秒內種子相同,產生重複數列 | 程式啟動時 僅呼叫一次 srand((unsigned)time(nullptr)) |
直接用 rand() % N | 模數偏差:當 RAND_MAX 不可被 N 整除時,分布不均 | 使用拒絕採樣或改用 <random> 分布器 |
多執行緒共用 rand() | 非執行緒安全,內部狀態競爭 | 各執行緒使用獨立引擎實例(C++11) |
現代 C++11 <random> 最佳實踐
標準庫提供「隨機數引擎」+「分布器」的組合設計,分離隨機來源與統計分布,支援高品質、執行緒安全、可移植的隨機數生成。
核心組件
- 引擎:
std::mt19937(梅森旋轉,推薦通用)、std::random_device(非決定性熵源,用於播種)。
- 分布器:
uniform_int_distribution、uniform_real_distribution、normal_distribution 等,保證統計特性。
標準範例:產生 [min, max] 區間均勻整數
#include <random>
#include <iostream>
int main() {
std::random_device rd; // 硬體熵源
std::mt19937 gen(rd()); // 引擎播種
std::uniform_int_distribution<> dis(1, 100); // 閉區間 [1,100]
for (int i = 0; i < 10; ++i)
std::cout << dis(gen) << ' ';
}
進階技巧
- 執行緒區域儲存:
thread_local std::mt19937 gen(std::random_device{}()); 避免鎖競爭。
- 可重現除錯:測試時用固定種子
std::mt19937 gen(42);。
- 浮點數區間:
std::uniform_real_distribution<double> dis(0.0, 1.0); 產生 [0,1)。
- 正態分布:
std::normal_distribution<> dis(mean, stddev);。
遷移建議
- 新專案直接使用
<random>,勿再碰 rand()。
- 舊代碼維護期可保留
rand(),但新增功能改用現代設施。
- 加密級隨機數需用作業系統 API(
/dev/urandom、CryptGenRandom、std::random_device 視實作而定)。
總結
rand() 時代已過,C++11 <random> 以「引擎+分布」架構解決了品質、偏差、執行緒安全、可移植性等全方位問題。掌握 std::mt19937 + uniform_int_distribution 組合,即可覆蓋 95% 以上的應用場景,寫出專業級的隨機數代碼。
- 上一篇文章: C++ 引用與指標的核心差異深度解析
- 下一篇文章: Win32 串口通訊編程四大方案深度解析
Copyright ©
程式碼之家 All Rights Reserved