早期嘗試採用「隨機抽取並移除」方式,但易產生邊界偏差(例如數字 2 後總跟著 1)。改用 費雪-耶茨演算法,可在 O(n) 時間內完成原地打亂,且機率分布均勻,是洗牌、抽獎、測試資料生成的標準作法。
using System;
public class RandomShuffler
{
private readonly Random _random = new Random();
///
/// 將傳入的整數陣列原地隨機打亂
///
public void Shuffle(int[] array)
{
if (array == null || array.Length <= 1) return;
for (int i = array.Length - 1; i > 0; i--)
{
int randomIndex = _random.Next(0, i + 1); // 0 ~ i 含邊界
// 交換 array[i] 與 array[randomIndex]
int temp = array[i];
array[i] = array[randomIndex];
array[randomIndex] = temp;
}
}
}
var numbers = new int[20];
for (int i = 0; i < 20; i++) numbers[i] = i + 1; // 1~20
var shuffler = new RandomShuffler();
shuffler.Shuffle(numbers);
// 輸出結果
Console.WriteLine(string.Join(", ", numbers));
_random.Next(0, i) 應為 _random.Next(0, i + 1),否則最後一個元素永不被交換。new Random() 會因種子相同產生相同序列,請共用單一實例。Random 非執行緒安全,多執行緒環境請改用 ThreadLocal<Random> 或 lock 保護。public static class ShuffleExtensions
{
private static readonly Random _random = new Random();
public static void Shuffle<T>(this T[] array)
{
if (array == null || array.Length <= 1) return;
for (int i = array.Length - 1; i > 0; i--)
{
int j = _random.Next(0, i + 1);
(array[i], array[j]) = (array[j], array[i]); // C# 7+ tuple 交換
}
}
}
如此即可對 string[]、CustomObject[] 等任意陣列呼叫 array.Shuffle()。