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

C 語言隨機數生成深度解析:srand、rand 與 time 協作機制

     編輯:關於C語言

前言

「隨機數」是程式設計常見需求,但 C 標準庫提供的 rand() 與 srand() 機制常被誤用,導致「每次執行序列相同」或「循環周期極短」。本文深入剖析偽隨機數原理、種子設置陷阱、正確用法與進階替代方案。

核心概念釐清

術語定義
偽隨機數確定性演算法產生,滿足統計隨機性,但可重現。真隨機需硬體熵源(熱雜訊、輻射衰變等)
種子演算法初始狀態。相同種子 → 相同序列。srand(seed) 設定種子,rand() 依種子產生數列
RAND_MAXrand() 最大回傳值(至少 32767,常為 2147483647)

標準庫函式詳解

1. void srand(unsigned int seed) — 設定種子

  • 程式啟動時若未呼叫 srand(),等同 srand(1)
  • 種子必須「變化」才能產生不同序列
  • 常見做法:srand((unsigned)time(NULL))

2. int rand(void) — 取得偽隨機數

  • 回傳 0 ~ RAND_MAX 間整數
  • 內部為線性同餘法(LCG)或更佳演算法,具週期性
  • 關鍵:rand() 本身不會重新呼叫 srand(),種子只在 srand() 顯式呼叫時設定

3. time_t time(time_t *timer) — 取得秒數當種子

  • 回傳 1970-01-01 00:00:00 UTC 至今的秒數
  • 精度只到秒,同一秒內多次啟動種子相同

正確使用模式

#include <stdlib.h>
#include <time.h>

int main(void) {
    /* 1. 程式啟動時「只呼叫一次」srand */
    srand((unsigned)time(NULL));

    /* 2. 後續只呼叫 rand(),不要重複 srand */
    for (int i = 0; i < 10; i++) {
        int r = rand() % 10;        /* 0~9,但有模數偏差 */
        int r_better = rand() / (RAND_MAX / 10 + 1);  /* 均勻分布較佳 */
        printf("%d ", r);
    }
    puts("");
    return 0;
}

常見錯誤與修正

錯誤寫法後果修正
for(...) { srand(time(NULL)); r=rand(); }同一秒內種子相同,產生重複數列srand() 提到迴圈外,全程只呼叫一次
rand() % N 取範圍當 N 不整除 RAND_MAX+1 時,分布不均(模數偏差)用 rand() / (RAND_MAX / N + 1) 或拒絕採樣法
多執行緒共用 rand()資料競爭,序列交錯不可預期用 rand_r() (POSIX) 或 C11 thread_local 狀態,或 mt19937
加密用途用 rand()可預測,嚴重安全漏洞用 /dev/urandom、CryptGenRandom、getrandom()、std::random_device

進階替代方案

C11 <stdrandom.h>(草案階段) / C++11 <random>

/* C++11 範例,現代 C 專案常混編使用 */
#include <random>
std::mt19937 gen(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 100);
int r = dist(gen);  /* 高品質、無模數偏差、執行緒安全 */

POSIX random()/srandom() / drand48()

  • 週期更長、統計特性優於 rand()
  • random_r() 提供可重入版本

密碼學級隨機數

  • Linux: getrandom(2)、/dev/urandom
  • Windows: BCryptGenRandom()、CryptGenRandom()
  • 跨平台: libsodium randombytes_buf()

總結

  • srand(time(NULL)) 只在 main 開頭呼叫一次
  • 範圍縮放用 除法而非模數 避免偏差
  • 多執行緒/高品質/加密需求,棄用 rand() 改用現代 API
  • 理解 LCG 週期與統計缺陷,選擇適合場景的產生器
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved