Mischen Sie jede (I)List
mit einer Erweiterungsmethode basierend auf dem Fisher-Yates shuffle:
private static Random rng = new Random();
public static void Shuffle(this IList 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;
}
}
Verwendung:
List produkte = GetProducts();
produkte.Shuffle();
Der obige Code verwendet die stark kritisierte System.Random-Methode zur Auswahl von Austauschkandidaten. Es ist schnell, aber nicht so zufällig, wie es sein sollte. Wenn Sie eine bessere Qualität der Zufälligkeit in Ihren Mischungen benötigen, verwenden Sie den Zufallszahlengenerator in System.Security.Cryptography wie folgt:
using System.Security.Cryptography;
...
public static void Shuffle(this IList 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;
}
}
Ein einfacher Vergleich ist verfügbar auf diesem Blog (WayBack Machine).
Bearbeitung: Seitdem ich diese Antwort vor ein paar Jahren geschrieben habe, haben viele Leute kommentiert oder mir geschrieben, um den großen dummen Fehler in meinem Vergleich herauszustellen. Sie haben natürlich recht. Es ist nichts falsch mit System.Random, wenn es auf die beabsichtigte Weise verwendet wird. In meinem ersten obigen Beispiel instantiiere ich die rng-Variable innerhalb der Shuffle-Methode, was Probleme verursacht, wenn die Methode wiederholt aufgerufen wird. Unten finden Sie ein korrigiertes Beispiel auf der Grundlage eines wirklich nützlichen Kommentars, den ich heute von @weston hier im SO erhalten habe.
Program.cs:
using System;
using System.Collections.Generic;
using System.Threading;
namespace SimpleLottery
{
class Program
{
private static void Main(string[] args)
{
var zahlen = new List(Enumerable.Range(1, 75));
zahlen.Shuffle();
Console.WriteLine("Die Gewinnzahlen lauten: {0}", string.Join(", ", zahlen.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(this IList 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;
}
}
}
}
4 Stimmen
Es gibt ein offenes Problem, um diese Funktionalität in .NET zu integrieren: github.com/dotnet/corefx/issues/461
7 Stimmen
Du könntest an diesem NuGet-Paket interessiert sein, das Erweiterungsmethoden für das Mischen von IList und IEnumerable mithilfe des unten erwähnten Fisher-Yates-Algorithmus enthält
0 Stimmen
Es gibt auch verwandte Wählen Sie N zufällige Elemente aus einer Liste und Diskussion zu Shuffle mit OrderBy vs. Fisher-Yates.