Was ist der beste Weg, um die Reihenfolge einer generischen Liste in C # zufällig zu bestimmen? Ich habe einen endlichen Satz von 75 Zahlen in einer Liste, der ich eine zufällige Reihenfolge zuweisen möchte, um sie für eine Lotterieanwendung zu zeichnen.
c#
generic-list
mirezus
quelle
quelle
Antworten:
Mische alle
(I)List
mit einer Erweiterungsmethode, die auf dem Fisher-Yates-Shuffle basiert :Verwendungszweck:
Der obige Code verwendet die viel kritisierte System.Random-Methode, um Swap-Kandidaten auszuwählen. Es ist schnell, aber nicht so zufällig, wie es sein sollte. Wenn Sie eine bessere Qualität der Zufälligkeit in Ihren Mischvorgängen benötigen, verwenden Sie den Zufallszahlengenerator in System.Security.Cryptography wie folgt:
Ein einfacher Vergleich ist in diesem Blog (WayBack Machine) verfügbar .
Bearbeiten: Seit ich diese Antwort vor ein paar Jahren geschrieben habe, haben viele Leute mich kommentiert oder geschrieben, um auf den großen dummen Fehler in meinem Vergleich hinzuweisen. Sie haben natürlich recht. Es ist nichts falsch mit System.Random, wenn es so verwendet wird, wie es beabsichtigt war. In meinem ersten Beispiel oben instanziiere ich die Variable rng innerhalb der Shuffle-Methode, die nach Problemen fragt, wenn die Methode wiederholt aufgerufen werden soll. Unten finden Sie ein festes, vollständiges Beispiel, das auf einem wirklich nützlichen Kommentar basiert, den @weston heute hier auf SO erhalten hat.
Program.cs:
quelle
Random rng = new Random();
ein zu machenstatic
würde das Problem im Vergleichspost lösen. Da jeder nachfolgende Anruf dem letzten zufälligen Ergebnis des vorherigen Anrufs folgen würde.Wenn wir nur Artikel in einer völlig zufälligen Reihenfolge mischen müssen (nur um die Artikel in einer Liste zu mischen), bevorzuge ich diesen einfachen, aber effektiven Code, der Artikel nach Guid ...
quelle
var shuffledcards = cards.OrderBy(a => rng.Next());
compilr.com/grenade/sandbox/Program.csNewGuid
garantiert nur, dass Sie eine eindeutige GUID erhalten. Es gibt keine Garantie für Zufälligkeit. Wenn Sie eine GUID für einen anderen Zweck als die Erstellung eines eindeutigen Werts verwenden, machen Sie es falsch.Ich bin etwas überrascht von all den klobigen Versionen dieses einfachen Algorithmus hier. Fisher-Yates (oder Knuth Shuffle) ist etwas knifflig, aber sehr kompakt. Warum ist es schwierig? Weil Sie darauf achten müssen, ob Ihr Zufallszahlengenerator einen
r(a,b)
Wert zurückgibt, derb
inklusive oder exklusiv ist. Ich habe auch die Wikipedia-Beschreibung bearbeitet, damit die Leute dort nicht blindlings dem Pseudocode folgen und schwer zu erkennende Fehler erstellen. Gibt für .Net ohne weiteres eineRandom.Next(a,b)
exklusive Nummerb
zurück. So kann es in C # /. Net implementiert werden:Versuchen Sie diesen Code .
quelle
i = list.Count - 1
, dh die letzte Iteration,rnd.Next(i, list.Count)
gibt Ihnen i zurück. Sie benötigen daheri < list.Count -1
als Schleifenbedingung. Nun, du brauchst es nicht, aber es spart 1 Iteration;)Erweiterungsmethode für IEnumerable:
quelle
OrderBy
Sortiert die Elemente mithilfe einer QuickSort-Variante nach ihren (scheinbar zufälligen) Schlüsseln. Die QuickSort-Leistung beträgt O (N log N) . Im Gegensatz dazu ist ein Fisher-Yates-Shuffle O (N) . Bei einer Sammlung von 75 Elementen ist dies möglicherweise keine große Sache, aber der Unterschied wird bei größeren Sammlungen deutlich.Random.Next()
kann eine einigermaßen pseudozufällige Verteilung von Werten erzeugen, garantiert jedoch nicht , dass die Werte eindeutig sind. Die Wahrscheinlichkeit doppelter Schlüssel wächst (nicht linear) mit N, bis sie die Gewissheit erreicht, wenn N 2 ^ 32 + 1 erreicht. DerOrderBy
QuickSort ist eine stabile Sorte. Wenn also mehreren Elementen zufällig derselbe Pseudozufallsindexwert zugewiesen wird, ist ihre Reihenfolge in der Ausgabesequenz dieselbe wie in der Eingabesequenz. somit wird eine Vorspannung in das "Mischen" eingeführt.Die Idee ist, ein anonymes Objekt mit Artikel und zufälliger Reihenfolge zu erhalten und die Artikel dann nach dieser Reihenfolge und dem Rückgabewert neu zu ordnen:
quelle
quelle
var listCopy = list.ToList()
vermeiden, dass alle Elemente von der eingehenden Liste gestrichen werden? Ich verstehe nicht wirklich, warum Sie diese Listen zum Leeren mutieren möchten.BEARBEITEN Das
RemoveAt
ist eine Schwäche in meiner vorherigen Version. Diese Lösung überwindet das.Beachten Sie die Option
Random generator
: Wenn die Basis-Framework-Implementierung vonRandom
nicht threadsicher oder kryptografisch stark genug für Ihre Anforderungen ist, können Sie Ihre Implementierung in den Vorgang einfügen.Eine geeignete Implementierung für eine thread-sichere kryptografisch starke
Random
Implementierung finden Sie in dieser Antwort.Hier ist eine Idee, IList auf (hoffentlich) effiziente Weise zu erweitern.quelle
GetNext
oderNext
?Sie können dies mit dieser einfachen Erweiterungsmethode erreichen
und Sie können es verwenden, indem Sie die folgenden Schritte ausführen
quelle
Random
Klasseninstanz außerhalb der Funktion alsstatic
Variable halten. Andernfalls erhalten Sie möglicherweise denselben Randomisierungs-Seed vom Timer, wenn Sie schnell hintereinander aufgerufen werden.Dies ist meine bevorzugte Methode für ein Mischen, wenn es wünschenswert ist, das Original nicht zu ändern. Es ist eine Variante des Fisher-Yates-Algorithmus "Inside-Out" , der mit jeder aufzählbaren Sequenz funktioniert (die Länge von
source
muss nicht von Anfang an bekannt sein).Dieser Algorithmus kann auch implementiert werden, indem ein Bereich von
0
bislength - 1
zugewiesen und die Indizes zufällig erschöpft werden, indem der zufällig ausgewählte Index gegen den letzten Index ausgetauscht wird, bis alle Indizes genau einmal ausgewählt wurden. Dieser obige Code führt genau das Gleiche aus, jedoch ohne die zusätzliche Zuordnung. Welches ist ziemlich ordentlich.In Bezug auf die
Random
Klasse handelt es sich um einen Allzweck-Zahlengenerator (und wenn ich eine Lotterie betreiben würde, würde ich in Betracht ziehen, etwas anderes zu verwenden). Standardmäßig wird auch ein zeitbasierter Startwert verwendet. Eine kleine Lösung des Problems besteht darin, dieRandom
Klasse mit dem zu setzen,RNGCryptoServiceProvider
oder Sie könnten dasRNGCryptoServiceProvider
in einer ähnlichen Methode (siehe unten) verwenden, um einheitlich ausgewählte zufällige doppelte Gleitkommawerte zu generieren, aber um eine Lotterie durchzuführen, müssen Sie die Zufälligkeit und die Art von verstehen die Zufallsquelle.Der Punkt des Erzeugens eines zufälligen Doppels (ausschließlich zwischen 0 und 1) besteht darin, auf eine ganzzahlige Lösung zu skalieren. Wenn Sie etwas aus einer Liste auswählen müssen, das auf einem zufälligen Double basiert
x
, ist dies immer0 <= x && x < 1
einfach.Genießen!
quelle
Wenn es Ihnen nichts ausmacht, zwei zu verwenden
Lists
, ist dies wahrscheinlich der einfachste Weg, aber wahrscheinlich nicht der effizienteste oder unvorhersehbarste:quelle
Wenn Sie eine feste Zahl (75) haben, können Sie ein Array mit 75 Elementen erstellen, dann Ihre Liste auflisten und die Elemente an zufällige Positionen im Array verschieben. Sie können die Zuordnung der Listennummer zum Array-Index mithilfe der Fisher-Yates-Zufallswiedergabe generieren .
quelle
Ich benutze normalerweise:
quelle
quelle
Hier ist ein effizienter Shuffler, der ein Byte-Array von Shuffled-Werten zurückgibt. Es mischt nie mehr als nötig. Es kann an der Stelle neu gestartet werden, an der es zuvor aufgehört hat. Meine eigentliche Implementierung (nicht gezeigt) ist eine MEF-Komponente, die einen benutzerdefinierten Ersatzmischer ermöglicht.
`
quelle
Hier ist eine thread-sichere Möglichkeit, dies zu tun:
quelle
quelle
Eine einfache Änderung der akzeptierten Antwort , die eine neue Liste zurückgibt, anstatt direkt zu arbeiten, und die allgemeinere akzeptiert,
IEnumerable<T>
wie dies bei vielen anderen Linq-Methoden der Fall ist.quelle
Ich habe online eine interessante Lösung gefunden.
Mit freundlicher Genehmigung: https://improveandrepeat.com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/
var shuffled = myList.OrderBy (x => Guid.NewGuid ()). ToList ();
quelle
Sicher alter Beitrag, aber ich benutze nur eine GUID.
Eine GUID ist immer eindeutig, und da sie jedes Mal neu generiert wird, ändert sich das Ergebnis jedes Mal.
quelle
Ein sehr einfacher Ansatz für diese Art von Problem besteht darin, eine Reihe von zufälligen Elementtauschvorgängen in der Liste zu verwenden.
Im Pseudocode würde dies so aussehen:
quelle