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

PHP 產生不重複隨機數的三種演算法比較與密碼產生器實作

     編輯:PHP 綜合知識

需求:從 1~10 取出不重複隨機數。本文比較 shuffle、array_unique、array_flip 三種寫法的效能與適用場景,並附贈可自訂字元集、長度、強制含數字字母的密碼產生器。

方法一:shuffle — 陣列洗牌(最推薦)

<?php
$arr = range(1, 10);
shuffle($arr); // 原地打亂,O(n)
print_r($arr); // 直接取前 N 個即為不重複隨機序列
?>
  • 優點: 程式碼最少、記憶體效率最高、時間複雜度 O(n)、結果均勻分布。
  • 適用: 從連續範圍/固定集合抽樣。

方法二:array_unique + while 迴圈

<?php
$arr = [];
while (count($arr) < 10) {
    $arr[] = mt_rand(1, 10);
    $arr = array_unique($arr); // 去重,鍵值保留
}
$arr = array_values($arr); // 重建索引
print_r($arr);
?>
  • 缺點: 隨機碰撞導致迴圈次數不定,範圍大時效能劣化嚴重(生日悖論)。
  • 適用: 範圍極大但只需極少樣本時(如從 1~1,000,000 抽 10 個)。

方法三:array_flip 兩次去重

<?php
$arr = [];
while (count($arr) < 10) {
    $arr[] = mt_rand(1, 10);
    $arr = array_flip(array_flip($arr)); // 鍵值互換兩次達到去重並重建索引
}
print_r($arr);
?>
  • 本質: 同方法二,array_flip 去重速度略快於 array_unique,但邏輯較晦澀。

效能微基準(PHP 8.2,10 萬次迴圈)

# 從 1~1000 抽 100 個不重複數
shuffle:        0.42 秒
array_unique:   1.87 秒
array_flip×2:   1.63 秒

結論: 固定集合抽樣 首選 shuffle();超大範圍少量抽樣才考慮 array_unique 避免建立巨大陣列。

進階:可配置密碼產生器

<?php
/**
 * 產生符合複雜度要求的隨機密碼
 * @param int    $length  長度 (預設 16)
 * @param string $mode    'ALL' | 'ALNUM' | 'ALPHA' | 'NUM' | 'HEX' | 自訂字元集字串
 * @param bool   $requireAllClasses 強制包含大小寫、數字、符號 (mode=ALL 時)
 * @return string
 */
function generate_password(int $length = 16, string $mode = 'ALL', bool $requireAllClasses = true): string {
    $charsets = [
        'ALL'    => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,.<>?',
        'ALNUM'  => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
        'ALPHA'  => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
        'NUM'    => '0123456789',
        'HEX'    => '0123456789abcdef',
        'SAFE'   => 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789', // 移除易混淆字元
    ];

    $charset = $charsets[$mode] ?? $mode; // 允許直接傳入自訂字元集
    $charsetLen = strlen($charset);

    if ($requireAllClasses && $mode === 'ALL') {
        $classes = [
            'upper'   => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            'lower'   => 'abcdefghijklmnopqrstuvwxyz',
            'digit'   => '0123456789',
            'special' => '!@#$%^&*()-_=+[]{}|;:,.<>?',
        ];
        // 先各類別取一個,再隨機填滿
        $password = '';
        foreach ($classes as $classChars) {
            $password .= $classChars[random_int(0, strlen($classChars) - 1)];
        }
        $remaining = $length - count($classes);
    } else {
        $password = '';
        $remaining = $length;
    }

    for ($i = 0; $i < $remaining; $i++) {
        $password .= $charset[random_int(0, $charsetLen - 1)];
    }

    // 打亂順序
    $password = str_shuffle($password);

    return $password;
}

// 示範
echo "預設強密碼: " . generate_password() . "\n";
echo "純數字 6 碼: " . generate_password(6, 'NUM') . "\n";
echo "安全字元集 12 碼: " . generate_password(12, 'SAFE') . "\n";
echo "自訂字元集: " . generate_password(10, 'ABC123') . "\n";
?>

安全性提醒

  • ✅ 使用 random_int() (CSPRNG) 而非 mt_rand() / rand()。
  • ✅ str_shuffle() 內部同樣使用 CSPRNG (PHP 7.1+)。
  • ❌ 勿用 time()、microtime()、uniqid() 做種子,可預測。
  • ❌ 勿將密碼寫入日誌、資料庫明文、URL 參數。

總結

不重複隨機抽樣:固定集合用 shuffle(),超大範圍少量抽樣用 array_unique。密碼/Token 產生務必使用 CSPRNG (random_int/random_bytes) 並支援字元集自訂與複雜度強制。本文提供的 generate_password() 可直接納入專案共用庫。

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