需求:從 1~25 中隨機抽取 16 個不重複數字(模擬投票場景)。以下提供高效能解法並解析關鍵技巧。
/**
* 產生不重複隨機整數陣列
* @param int $min 最小值(含)
* @param int $max 最大值(含)
* @param int $num 需要數量
* @return array 索引連續的整數陣列
*/
function unique_rand(int $min, int $max, int $num): array {
if ($num > ($max - $min + 1)) {
throw new InvalidArgumentException("請求數量超過範圍");
}
$result = [];
while (count($result) < $num) {
$result[] = mt_rand($min, $max);
// 「翻翻法」去重:利用鍵值互換特性,比 array_unique() 快數倍
$result = array_flip(array_flip($result));
}
shuffle($result); // 打亂順序並重置鍵名為 0~n-1
return $result;
}
// 使用範例
$arr = unique_rand(1, 25, 16);
sort($arr); // 如需排序
echo implode(',', $arr);
// 輸出範例:2,3,4,6,7,8,9,10,11,12,13,16,20,21,22,24
| 技巧 | 說明 | 效能優勢 |
|---|---|---|
mt_rand() | 梅森旋轉演算法,速度約 rand() 4 倍,隨機品質更好 | 高 |
翻翻法 array_flip(array_flip($arr)) | 利用陣列鍵名唯一性去重,避免 array_unique() 內部排序開銷 | 極高(大陣列下可快 10x+) |
shuffle() | 打亂順序並重建連續數字鍵,避免去重後鍵名斷裂導致遍歷麻煩 | 必要步驟 |
// 當範圍不大(如 1~1000),建立完整陣列洗牌切片更快
function unique_rand_small_range(int $min, int $max, int $num): array {
$pool = range($min, $max);
shuffle($pool);
return array_slice($pool, 0, $num);
}
mt_rand() 已修正偏差問題,可放心使用。range() 會佔用大量記憶體,改用集合(如 SplFixedArray)或資料庫 ORDER BY RAND() LIMIT n。random_int()。