Was ist der beste Weg, um ein Array von Zeichenfolgen mit .NET zu randomisieren? Mein Array enthält ungefähr 500 Zeichenfolgen und ich möchte eine neue Array
mit denselben Zeichenfolgen erstellen, jedoch in zufälliger Reihenfolge.
Bitte fügen Sie Ihrer Antwort ein C # -Beispiel bei.
myArray.Shuffled().ToArray()
(odermyArray.Shuffle()
wenn Sie das aktuelle Array mutieren möchten)Antworten:
Wenn Sie mit .NET 3.5 arbeiten, können Sie die folgende IEnumerable-Coolness verwenden (VB.NET, nicht C #, aber die Idee sollte klar sein ...):
Bearbeiten: OK und hier ist der entsprechende VB.NET-Code:
Zweite Bearbeitung als Antwort auf die Bemerkung, dass System.Random aufgrund der Rückgabe einer zeitbasierten Sequenz "nicht threadsicher" und "nur für Spielzeug-Apps geeignet" ist: Wie in meinem Beispiel verwendet, ist Random () absolut threadsicher, es sei denn Sie lassen zu, dass die Routine, in der Sie das Array randomisieren, erneut eingegeben wird. In diesem Fall benötigen Sie
lock (MyRandomArray)
sowieso etwas , um Ihre Daten nicht zu beschädigen, was ebenfalls den Schutz schütztrnd
.Es sollte auch klar sein, dass System.Random als Entropiequelle nicht sehr stark ist. Wie in der MSDN-Dokumentation angegeben , sollten Sie etwas verwenden, das von abgeleitet ist,
System.Security.Cryptography.RandomNumberGenerator
wenn Sie sicherheitsrelevante Maßnahmen ergreifen. Beispielsweise:...
...
quelle
OrderBy
die Sortierschlüssel nicht intern zwischengespeichert werden, besteht auch das Problem, dass die transitiven Eigenschaften geordneter Vergleiche verletzt werden. Wenn es jemals eine Überprüfung des Debug-Modus gibt,OrderBy
die korrekte Ergebnisse liefert, kann dies theoretisch eine Ausnahme auslösen.Die folgende Implementierung verwendet den Fisher-Yates-Algorithmus AKA the Knuth Shuffle. Es läuft in O (n) -Zeit und mischt an Ort und Stelle, ist also leistungsfähiger als die Technik "Nach Zufall sortieren", obwohl es sich um mehr Codezeilen handelt. Sehen Sie hier für einige vergleichende Leistungsmessungen. Ich habe System.Random verwendet, was für nicht kryptografische Zwecke in Ordnung ist. *
Verwendung:
* Für längere Arrays wäre es notwendig, einen Pseudozufallszahlengenerator (PRNG) durch viele Iterationen für jeden Swap zu führen, um die (extrem große) Anzahl von Permutationen gleich wahrscheinlich zu machen, um genügend Entropie zu erzeugen. Für ein 500-Elemente-Array nur ein sehr kleiner Bruchteil der möglichen 500! Permutationen können mit einem PRNG erhalten werden. Trotzdem ist der Fisher-Yates-Algorithmus unvoreingenommen und daher ist das Mischen so gut wie das von Ihnen verwendete RNG.
quelle
array.Shuffle(new Random());
.. zu machen?new Random()
Startwert mit einem Startwert basierend auf der aktuellen Systemzeit initialisiert wird, der nur alle ~ 16 ms aktualisiert wird.Sie suchen nach einem Mischalgorithmus, oder?
Okay, es gibt zwei Möglichkeiten, dies zu tun: Die Schlauen, aber die Leute scheinen es immer falsch zu verstehen und es falsch zu verstehen, also ist es vielleicht doch nicht so klug Weg, und der stumm wie Felsen, aber wen interessiert das, weil es funktioniert.
Blöder Weg
Dieser Algorithmus funktioniert gut, aber stellen Sie sicher, dass Ihr Zufallszahlengenerator wahrscheinlich nicht zwei Zeichenfolgen mit derselben Nummer markiert. Aufgrund des sogenannten Geburtstagsparadoxons geschieht dies häufiger als erwartet. Seine zeitliche Komplexität beträgt O ( n log n ).
Kluger Weg
Ich werde dies als rekursiven Algorithmus beschreiben:
Das iterative Äquivalent besteht darin, einen Iterator durch das Array zu führen und dabei mit zufälligen Elementen zu tauschen. Beachten Sie jedoch, dass Sie nicht mit einem Element nach dem Element tauschen können , auf das der Iterator zeigt. Dies ist ein sehr häufiger Fehler und führt zu einem voreingenommenen Shuffle.
Die zeitliche Komplexität ist O ( n ).
quelle
Dieser Algorithmus ist einfach, aber nicht effizient, O (N 2 ). Alle "Order by" -Algorithmen sind typischerweise O (N log N). Unter Hunderttausenden von Elementen macht es wahrscheinlich keinen Unterschied, aber bei großen Listen.
Der Grund, warum es O (N 2 ) ist, ist subtil: List.RemoveAt () ist eine O (N) -Operation, es sei denn, Sie entfernen in der Reihenfolge vom Ende.
quelle
Sie können auch eine Erweiterungsmethode aus Matt Howells erstellen. Beispiel.
Dann können Sie es einfach so verwenden:
quelle
Das Randomisieren des Arrays ist intensiv, da Sie eine Reihe von Zeichenfolgen verschieben müssen. Warum nicht einfach zufällig aus dem Array lesen? Im schlimmsten Fall können Sie sogar eine Wrapper-Klasse mit getNextString () erstellen. Wenn Sie wirklich ein zufälliges Array erstellen müssen, können Sie so etwas tun
Die * 5 ist beliebig.
quelle
Wenn Sie nur an meinen Kopf denken, können Sie dies tun:
quelle
Generieren Sie ein Array von zufälligen Floats oder Ints gleicher Länge. Sortieren Sie dieses Array und führen Sie entsprechende Swaps für Ihr Zielarray durch.
Dies ergibt eine wirklich unabhängige Sorte.
quelle
quelle
Jacco, Ihre Lösung für einen benutzerdefinierten IComparer ist nicht sicher. Die Sortierroutinen erfordern, dass der Vergleicher mehrere Anforderungen erfüllt, um ordnungsgemäß zu funktionieren. An erster Stelle steht dabei die Konsistenz. Wenn der Vergleicher für dasselbe Objektpaar aufgerufen wird, muss er immer dasselbe Ergebnis zurückgeben. (Der Vergleich muss auch transitiv sein).
Die Nichterfüllung dieser Anforderungen kann zu einer Reihe von Problemen in der Sortierroutine führen, einschließlich der Möglichkeit einer Endlosschleife.
In Bezug auf die Lösungen, die jedem Eintrag einen zufälligen numerischen Wert zuordnen und dann nach diesem Wert sortieren, führen diese zu einer inhärenten Verzerrung in der Ausgabe, da jedes Mal, wenn zwei Einträgen der gleiche numerische Wert zugewiesen wird, die Zufälligkeit der Ausgabe beeinträchtigt wird. (In einer "stabilen" Sortierroutine ist das, was zuerst in der Eingabe steht, das erste in der Ausgabe. Array.Sort ist zwar nicht stabil, aber es gibt immer noch eine Verzerrung, die auf der vom Quicksort-Algorithmus vorgenommenen Partitionierung basiert.)
Sie müssen sich überlegen, welchen Grad an Zufälligkeit Sie benötigen. Wenn Sie eine Pokerseite betreiben, auf der Sie kryptografische Zufallsstufen benötigen, um sich vor einem entschlossenen Angreifer zu schützen, haben Sie ganz andere Anforderungen als jemand, der nur eine Song-Wiedergabeliste zufällig auswählen möchte.
Für das Mischen von Songlisten gibt es kein Problem mit einem gesetzten PRNG (wie System.Random). Für eine Pokerseite ist dies nicht einmal eine Option und Sie müssen viel schwieriger über das Problem nachdenken, als irgendjemand bei einem Stackoverflow für Sie tun wird. (Die Verwendung eines kryptografischen RNG ist nur der Anfang. Sie müssen sicherstellen, dass Ihr Algorithmus keine Verzerrung einführt, dass Sie über ausreichende Entropiequellen verfügen und dass Sie keinen internen Zustand offenlegen, der die nachfolgende Zufälligkeit beeinträchtigen würde.)
quelle
Dieser Beitrag wurde bereits ziemlich gut beantwortet - verwenden Sie eine Durstenfeld-Implementierung des Fisher-Yates-Shuffle für ein schnelles und unvoreingenommenes Ergebnis. Es wurden sogar einige Implementierungen veröffentlicht, obwohl ich feststelle, dass einige tatsächlich falsch sind.
Ich habe vor einiger Zeit einige Posts über die Implementierung von vollständigen und teilweisen Shuffles mit dieser Technik geschrieben und (über diesen zweiten Link möchte ich einen Mehrwert schaffen) auch einen Follow-up-Post darüber, wie Sie überprüfen können, ob Ihre Implementierung unvoreingenommen ist. Hiermit kann jeder Shuffle-Algorithmus überprüft werden. Sie können am Ende des zweiten Beitrags sehen, wie sich ein einfacher Fehler in der Zufallszahlenauswahl auswirken kann.
quelle
Ok, das ist eindeutig eine Beule von meiner Seite (entschuldigt sich ...), aber ich verwende oft eine ziemlich allgemeine und kryptografisch starke Methode.
Shuffle () ist eine Erweiterung für jede IEnumerable, mit der beispielsweise Zahlen von 0 bis 1000 in zufälliger Reihenfolge in einer Liste abgerufen werden können
Diese Methode wird auch beim Sortieren keine Überraschungen bereiten, da der Sortierwert genau einmal pro Element in der Sequenz generiert und gespeichert wird.
quelle
Sie benötigen keine komplizierten Algorithmen.
Nur eine einfache Zeile:
Beachten Sie, dass wir das
Array
in einList
erstes konvertieren müssen , wenn Sie es überhaupt nicht verwendenList
.Beachten Sie auch, dass dies für sehr große Arrays nicht effizient ist! Ansonsten ist es sauber und einfach.
quelle
Dies ist eine vollständig funktionierende Konsolenlösung, die auf dem hier aufgeführten Beispiel basiert :
quelle
quelle
Hier ist eine einfache Möglichkeit, OLINQ zu verwenden:
quelle
quelle
sortedList = source.ToList().OrderBy(x => generator.Next()).ToArray();