rand() / srand() 完全指南:從原理到工程級用法
基本用法
#include <stdlib.h>
#include <time.h>
int main() {
srand((unsigned)time(NULL)); // 種子:當前時間秒數
for (int i = 0; i < 10; ++i)
printf("%d ", rand() % 100); // 0-99
return 0;
}
核心機制
rand():線性同餘生成器 (LCG),返回 0 到 RAND_MAX (至少 32767) 偽隨機數。
srand(seed):設置種子,**相同種子產生完全相同序列** (可復現調試)。
- 不調用 srand:默認種子為 1,每次運行結果相同。
- 全局狀態:
rand() 非線程安全,多線程競爭種子/狀態。
常見陷阱與修正
| 錯誤寫法 | 問題 | 修正 |
rand() % N | 模數偏差:RAND_MAX+1 不能被 N 整除,低位數字概率略高 | 拒絕採樣或用浮點縮放 (int)(rand() / (RAND_MAX+1.0) * N) |
循環中反覆 srand(time(NULL)) | 同一秒內種子相同,產生相同序列 | **程序啟動時只調用一次 srand** |
多線程共用 rand() | 數據競爭,序列混亂 | 用 rand_r(&seed) (POSIX) 或 C++11 std::mt19937 線程局部引擎 |
加密/安全場景用 rand() | 可預測,非加密安全 | 用 /dev/urandom, CryptGenRandom, getrandom(), arc4random() |
現代 C/C++ 最佳實踐
C11 線程安全:rand_r
unsigned int seed = (unsigned)time(NULL);
int r = rand_r(&seed); // 線程局部種子,無全局狀態
C++11 (強烈推薦 C++ 項目)
#include
std::mt19937 gen(std::random_device{}()); // 硬體熵源種子
std::uniform_int_distribution dist(0, 99);
for (int i=0; i<10; ++i) std::cout << dist(gen) << ' ';
// 支援正態、泊松、指數等多種分佈,極快、高質量、線程安全
C 語言高質量替代:PCG / Xorshift / SplitMix64 (單文件引入)
// SplitMix64 極簡實現
static uint64_t sm64_state;
uint64_t splitmix64() {
uint64_t z = (sm64_state += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
void sm64_seed(uint64_t s) { sm64_state = s; }
工程建議
- **測試/模擬可復現**:固定種子
srand(12345) 或 mt19937 gen(12345)。
- **均勻分佈**:優先用
uniform_int_distribution 或拒絕採樣,避免 %N 偏差。
- **並發**:每線程獨立引擎/種子,避免鎖競爭。
- **安全**:密鑰、Token、Session ID 必須用 CSPRNG (系統提供)。