首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言隨機數生成完全指南:rand、srand 與最佳實踐

     編輯:關於C語言

核心函數


#include 

int rand(void);           // 回傳 0 到 RAND_MAX 間的偽隨機數
void srand(unsigned int seed); // 設定隨機種子

RAND_MAX 至少 32767 (15 位),具體值實作定義 (常見 2147483647)。

基本用法與陷阱

1. 不呼叫 srand() = 固定序列


for (int i = 0; i < 10; i++) printf("%d ", rand() % 10);
// 每次執行輸出相同:1 7 4 0 9 4 8 8 2 4 ...

等同於 srand(1)。必須呼叫 srand() 設定種子。

2. 固定種子 = 可重現序列 (除錯/測試用)


srand(5);
for (int i = 0; i < 10; i++) printf("%d ", rand() % 10);
// 每次輸出固定:4 3 5 9 0 0 7 9 2 0 ...

3. 時間種子 = 每次不同 (生產環境標準做法)


#include 
srand((unsigned)time(NULL)); // 以當前秒數為種子
for (int i = 0; i < 10; i++) printf("%d ", rand() % 10);
// 每次執行結果不同

注意:若程式在同一秒內多次啟動 (如腳本循環),種子相同 → 序列相同。高頻場景需結合 clock()、getpid()、/dev/urandom 等熵源。

範圍控制:生成 [min, max] 區間隨機整數

錯誤做法:取模偏差


rand() % (max - min + 1) + min; // 當 RAND_MAX 非 (max-min+1) 倍數時,分布不均

例如 RAND_MAX=32767,取模 10,0-6 出現 3277 次,7-9 出現 3276 次。小範圍影響微小,大範圍或嚴格均勻需求不可用。

正確做法:拒絕採樣


int rand_range(int min, int max) {
    int range = max - min + 1;
    int limit = RAND_MAX - (RAND_MAX % range); // 最大可接受值
    int r;
    do {
        r = rand();
    } while (r >= limit); // 拒絕超出範圍的值
    return min + r % range;
}

保證數學上完美均勻分布。

浮點數 [0.0, 1.0)


double rand_double(void) {
    return (double)rand() / (RAND_MAX + 1.0); // +1.0 確保 < 1.0
}

進階:更好的隨機數生成器

1. C11 <random> (C++ 標準庫,C 無原生支援)

C 語言標準庫僅提供 rand/srand (線性同餘法,統計品質差、週期短、不可預測性弱)。嚴肅應用請使用:

  • POSIX:random()/srandom() (BSD 改良)、drand48() 系列
  • Linux:getrandom() (系統呼叫,密碼學安全)、/dev/urandom
  • Windows:BCryptGenRandom() (CNG API)
  • 跨平台庫:OpenSSL RAND_bytes()、PCG、xoshiro256++ (單頭文件庫)

2. 簡易 xorshift32 實作 (無依賴、品質優於 rand)


static uint32_t xorshift_state = 1; // 必須非 0

uint32_t xorshift32(void) {
    uint32_t x = xorshift_state;
    x ^= x << 13;
    x ^= x >> 17;
    x ^= x << 5;
    return xorshift_state = x;
}

// 使用
srand(time(NULL)); xorshift_state = rand(); // 種子
for (int i = 0; i < 10; i++) printf("%u ", xorshift32() % 100);

週期 2³²-1,速度極快、統計特性良好、適合遊戲/模擬 (非加密用途)。

常見應用模式

洗牌演算法 (Fisher-Yates / Knuth Shuffle)


void shuffle(int *arr, int n) {
    for (int i = n - 1; i > 0; i--) {
        int j = rand_range(0, i); // 0 到 i 之間均勻
        int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
    }
}

原地、O(n)、無偏差。切勿用 for(i=0;i (有偏差)。

隨機挑選 k 個不重複元素 (水塘抽樣)


void reservoir_sample(int *stream, int n, int *sample, int k) {
    for (int i = 0; i < k; i++) sample[i] = stream[i];
    for (int i = k; i < n; i++) {
        int j = rand_range(0, i);
        if (j < k) sample[j] = stream[i];
    }
}

串流處理、記憶體 O(k)。

除錯與測試技巧

  1. 固定種子回歸測試:srand(12345) 確保測試可重現
  2. 統計檢驗:生成大量樣本做卡方檢驗、視覺化直方圖
  3. 邊界測試:驗證 rand_range(min, max) 含邊界、無越界
  4. 執行緒安全:rand() 非執行緒安全 (全域狀態),多執行緒用 rand_r(&seed) (POSIX) 或各執行緒獨立生成器實例

總結建議

場景建議
教學/簡單工具/原型srand(time(NULL)) + rand() + 拒絕採樣 rand_range
遊戲/模擬/科學計算xorshift32/64、PCG、xoshiro (單頭文件,零依賴)
密碼學/安全/金融OS 提供的 CSPRNG (getrandom, BCryptGenRandom, /dev/urandom)
跨平台庫開發抽象介面 random_bytes(void*, size_t),平台層實作

隨機數看似簡單,實則暗藏統計學與安全陷阱。選對工具、用對方法,才能讓「隨機」真正服務於你的程式邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved