Перемешать любое (I)List
с помощью метода расширения, основанного на перемешивании Фишера-Йейтса :
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Использование:
List<Product> products = GetProducts();
products.Shuffle();
Приведенный выше код использует критикуемый метод System.Random для выбора кандидатов на своп. Это быстро, но не так случайно, как должно быть. Если вам нужно более высокое качество случайности в случайном порядке, используйте генератор случайных чисел в System.Security.Cryptography следующим образом:
using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
byte[] box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
int k = (box[0] % n);
n--;
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Простое сравнение доступно в этом блоге (WayBack Machine).
Редактировать: После того, как я написал этот ответ пару лет назад, многие люди комментировали или писали мне, чтобы указать на большой глупый недостаток в моем сравнении. Они конечно правы. Нет ничего плохого в System.Random, если он используется так, как задумано. В моем первом примере, приведенном выше, я создаю экземпляр переменной rng внутри метода Shuffle, который вызывает проблемы, если метод будет вызываться повторно. Ниже приведен фиксированный, полный пример, основанный на действительно полезном комментарии, полученном сегодня от @weston здесь, на SO.
Program.cs:
using System;
using System.Collections.Generic;
using System.Threading;
namespace SimpleLottery
{
class Program
{
private static void Main(string[] args)
{
var numbers = new List<int>(Enumerable.Range(1, 75));
numbers.Shuffle();
Console.WriteLine("The winning numbers are: {0}", string.Join(", ", numbers.GetRange(0, 5)));
}
}
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}