rand() 是 C 標準庫提供的偽隨機數生成器,內部演算法實作由編譯器決定(通常為線性同餘法 LCG)。本文透過實測驗證其週期(G點)、跨平台差異,以及二次取模運算是否影響週期長度。
_INT_R 筆基準隨機序列存入陣列 rbase。rand(),每次與 rbase[0] 比對,若相同則進入逐項比對後續 _INT_R-1 筆。_INT_FZ 次輸出進度。| _INT_R (比對長度) | 偵測到的週期 | 換算公式 |
|---|---|---|
| 128 | 2,147,483,776 | 2,147,483,776 - 128 = 2,147,483,648 |
| 1024 | 2,147,484,672 | 2,147,484,672 - 1024 = 2,147,483,648 |
結論:Windows VS2015 rand() 週期為 231 = 2,147,483,648。
同樣邏輯跑數小時仍未偵測到週期,推測 glibc 實作週期極長(可能 264 級別),或採用更複雜演算法(如 Mersenne Twister 變體)。虛擬機效能較差,建議實體機長跑或改用統計檢測套件(TestU01、Dieharder)。
rand() % N 是否改變週期?修改測試程式將 rand() 回傳值先 % 100 再比對,結果 週期完全不變(仍為 2,147,483,648)。
推論:rand() 內部狀態遞迴與輸出值映射分離,取模僅為輸出端映射,不影響內部狀態機週期。
random(min, max)
#include <stdlib.h>
#include <assert.h>
#include <time.h>
/* 回傳 [min, max] 閉區間隨機整數 */
int random(int min, int max) {
assert(min < max);
return rand() % (max - min + 1) + min;
}
int main() {
srand((unsigned)time(NULL)); /* 僅程式啟動時呼叫一次 */
for (int i = 0; i < 100; i++) {
printf("%3d ", random(-5, 5));
if ((i + 1) % 10 == 0) putchar('\n');
}
return 0;
}
srand(time(NULL)) 僅在 main 開頭呼叫一次,切勿重複呼叫導致序列重置。rand() % N 當 RAND_MAX 非 N 倍數時,低位數分布略有偏差。高品質需求可用拒絕採樣或 random()/arc4random()。rand() 非執行緒安全,多執行緒請用 rand_r() 或 C++11 <random>。rand(),請改用 /dev/urandom、getrandom() 或 OpenSSL RAND_bytes。了解 rand() 週期與特性,有助於在遊戲掉寶、抽獎、模擬等場景做正確技術選型。