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

PHP生成指定範圍內不重複隨機數的多種方法

     編輯:PHP 相關知識

PHP 隨機數生成:從基礎 rand 到加密安全隨機數全解析

隨機數在驗證碼、抽獎、Token 生成、負載均衡等場景普遍使用。本文梳理 PHP 歷史函數演進、性能差異、安全性陷阱,並提供「指定範圍、不重複、多個」的最佳實踐代碼。

函數演進與選型

函數PHP 版本算法速度加密安全建議用途
rand()所有LCG (線性同餘)慢❌ 可預測❌ 棄用
mt_rand()4.2+Mersenne Twister快 (4倍 rand)❌ 狀態可逆推⚠️ 一般模擬、遊戲、非安全業務
random_int()7.0+系統 CSPRNG (getrandom/arc4random)中✅ 是✅ **首選**:Token、密碼、加密、抽獎
openssl_random_pseudo_bytes()5.3+OpenSSL CSPRNG中✅ 是✅ 7.0 前替代方案

場景一:獲取單個範圍隨機整數

<?php
// PHP 7.0+ 推薦 (加密安全)
$rand = random_int(1, 100); // 包含 1 和 100

// PHP 5.x 兼容
$rand = mt_rand(1, 100); // 非安全場景
// 或
$bytes = openssl_random_pseudo_bytes(4, $strong);
$rand = unpack('L', $bytes)[1] % 100 + 1; // 簡易映射,有模數偏差
?>

場景二:生成 N 個不重複隨機數 (核心需求)

策略:範圍較小 (如 1-100 取 10) → range + shuffle;範圍極大 (如 1-1億 取 100) → Set/Hash 去重循環。

<?php
/**
 * 獲取指定範圍內不重複的隨機整數數組
 * @param int $min 最小值
 * @param int $max 最大值
 * @param int $count 需要個數
 * @return array
 * @throws Exception 參數無效或範圍不足
 */
function getUniqueRandomInts(int $min, int $max, int $count): array {
    if ($count <= 0) return [];
    $range = $max - $min + 1;
    if ($count > $range) throw new Exception("請求數量 $count 超過範圍大小 $range");

    // 策略 A: 範圍較小時 (經驗值: range < 100000) 用 shuffle 最快
    if ($range <= 100000) {
        $pool = range($min, $max);
        shuffle($pool); // 原地打亂,O(N)
        return array_slice($pool, 0, $count);
    }

    // 策略 B: 範圍極大時,用 HashSet 去重循環
    $result = [];
    $seen = []; // 用作 Hash Set
    // 預分配大小優化 (PHP 8.1+)
    // $result = array_fill(0, $count, null); 
    
    while (count($result) < $count) {
        // 使用安全隨機源
        $num = random_int($min, $max);
        if (!isset($seen[$num])) {
            $seen[$num] = true;
            $result[] = $num;
        }
    }
    return $result;
}

// 使用示例
try {
    // 抽獎:1-10000 中抽 50 個幸運號
    $winners = getUniqueRandomInts(1, 10000, 50);
    // 驗證碼:6 位數字
    $code = implode('', getUniqueRandomInts(0, 9, 6)); // 注意:這會生成不重複數字,如需允許重複請用隨機選擇
    // Token: 16 字節十六進制 (更推薦 random_bytes)
    $token = bin2hex(random_bytes(16));
} catch (Exception $e) {
    error_log($e->getMessage());
}
?>

場景三:加權隨機抽取 (抽獎概率控制)

<?php
/**
 * 加權隨機選擇
 * @param array $items ['item_id' => weight, ...]
 * @return mixed 選中的 key
 */
function weightedRandom(array $items) {
    $total = array_sum($items);
    $rand = random_int(1, $total); // 1 到總權重
    
    foreach ($items as $key => $weight) {
        $rand -= $weight;
        if ($rand <= 0) return $key;
    }
    // 理論不會到達這裡
    return array_key_first($items);
}

// 獎品池: 總權重 10000
$prizes = [
    'grand_prize' => 1,      // 0.01%
    'first_prize' => 10,     // 0.1%
    'second_prize' => 100,   // 1%
    'third_prize' => 1000,   // 10%
    'consolation' => 8889,   // 88.89%
];

$win = weightedRandom($prizes);
echo "恭喜獲得: $win";
?>

常見誤區與避坑

  1. srand() / mt_srand() 手動播種:現代 PHP (4.2+) 自動播種,**切勿手動調用**,特別是用 microtime(),會極大降低熵,導致可預測。
  2. array_rand() 陷阱:PHP 7.1 前不使用 CSPRNG,且返回鍵名而非值,大數組性能差。**不推薦用於安全場景**。
  3. 模數偏差:random_bytes(4) % 100 會導致 0-59 概率略高於 60-99。正確做法:拒絕採樣或用 random_int。
  4. 浮點數隨機:lcg_value() / mt_rand() / mt_getrandmax() 精度有限,科學計算請用專用庫。

總結

  • **PHP 7.0+ 專案**:全場景統一用 random_int() (整數)、random_bytes() (字節串)。
  • **批量不重複**:小範圍 range+shuffle,大範圍 HashSet+random_int 循環。
  • **抽獎/加權**:權重累加 + random_int 單次抽取,公平高效。
  • **棄用**:rand(), srand(), uniqid() (非唯一、可預測)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved