C 標準庫提供 rand()/srand() 生成偽隨機數,本文系統講解種子機制、範圍控制、不重複隨機數生成等實戰技巧。
rand() 最大返回值(至少 32767)srand(seed) 設置初始狀態,未調用默認種子為 1#include
#include
#include
int main() {
// 以當前時間作種子,每次運行序列不同
srand((unsigned)time(NULL));
// 生成 10 個 0~RAND_MAX 隨機數
for (int i = 0; i < 10; i++)
printf("%d\n", rand());
return 0;
}
| 需求 | 公式 | 示例 |
|---|---|---|
| [0, 1] 浮點數 | rand() / (RAND_MAX + 1.0) | printf("%.4f", rand()/32768.0); |
| [min, max] 整數 | rand() % (max - min + 1) + min | rand() % 100 + 1 → [1,100] |
| [0, n-1] 整數 | rand() % n | rand() % 10 → [0,9] |
// 生成 [RANGE_MIN, RANGE_MAX] 區間隨機數
int RANGE_MIN = 0, RANGE_MAX = 100;
int rand100 = (int)(((double)rand() / (RAND_MAX + 1.0)) * (RANGE_MAX - RANGE_MIN + 1) + RANGE_MIN);
避免 rand() % n 導致的分佈偏態(當 n 不能整除 RAND_MAX+1 時)。
方法一:數組洗牌
void swap(int *a, int *b) { int t=*a; *a=*b; *b=t; }
int main() {
int a[513];
for (int i=1; i<=512; i++) a[i] = i; // 初始化有序數組
srand((unsigned)time(NULL));
for (int i=512; i>=1; i--)
swap(&a[i], &a[rand()%i + 1]); // 關鍵:+1 避免索引 0
// 前 64 個即為不重複隨機數
for (int i=1; i<=64; i++) printf("%4d", a[i]);
}
方法二:標記法(適用範圍較大、需少量數時)
int a[100] = {0};
for (int i=1; i<=99; i++) {
int m;
while (a[m = rand()%100 + 1]); // 找未被佔用的槽位
a[m] = i; // 標記並存入順序號
}
rand() 非線程安全,多線程用 rand_r() 或 C++11 <random>/dev/urandom 或 CryptGenRandomrand() 通常為線性同餘法,統計質量一般;科學計算建議用 Mersenne Twister (mt19937)#include
#include
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) << '\n';
}
總結:srand(time(NULL)) + rand()%n 適合簡單場景;嚴格均勻分佈用浮點映射;不重複需求用洗牌算法;生產級項目優先選用 C++11 <random>。