Quelle est la meilleure façon de randomiser l'ordre d'une liste générique en C #? J'ai un ensemble fini de 75 numéros dans une liste à laquelle je voudrais attribuer un ordre aléatoire, afin de les dessiner pour une application de type loterie.
c#
generic-list
mirezus
la source
la source
Réponses:
Mélangez tout
(I)List
avec une méthode d'extension basée sur le mélange Fisher-Yates :Usage:
Le code ci-dessus utilise la méthode System.Random très critiquée pour sélectionner les candidats à l'échange. C'est rapide mais pas aussi aléatoire qu'il devrait l'être. Si vous avez besoin d'une meilleure qualité d'aléatoire dans vos shuffles, utilisez le générateur de nombres aléatoires dans System.Security.Cryptography comme ceci:
Une comparaison simple est disponible sur ce blog (WayBack Machine).
Edit: Depuis que j'ai écrit cette réponse il y a quelques années, beaucoup de gens m'ont commenté ou écrit, pour souligner le gros défaut idiot de ma comparaison. Ils ont bien sûr raison. Il n'y a rien de mal avec System.Random s'il est utilisé de la manière prévue. Dans mon premier exemple ci-dessus, j'instancie la variable rng à l'intérieur de la méthode Shuffle, qui demande des problèmes si la méthode va être appelée à plusieurs reprises. Ci-dessous est un exemple fixe et complet basé sur un commentaire vraiment utile reçu aujourd'hui de @weston ici sur SO.
Program.cs:
la source
Random rng = new Random();
unstatic
résoudrait le problème dans le post de comparaison. Comme chaque appel suivant ferait suite aux appels précédents, dernier résultat aléatoire.Si nous avons seulement besoin de mélanger les éléments dans un ordre complètement aléatoire (juste pour mélanger les éléments dans une liste), je préfère ce code simple mais efficace qui commande les éléments par guid ...
la source
var shuffledcards = cards.OrderBy(a => rng.Next());
compilr.com/grenade/sandbox/Program.csNewGuid
garantit seulement qu'il vous donne un GUID unique. Il ne fait aucune garantie sur le caractère aléatoire. Si vous utilisez un GUID à des fins autres que la création d'une valeur unique , vous le faites mal.Je suis un peu surpris par toutes les versions maladroites de cet algorithme simple ici. Fisher-Yates (ou Knuth shuffle) est un peu délicat mais très compact. Pourquoi est-ce difficile? Parce que vous devez faire attention à ce que votre générateur de nombres aléatoires
r(a,b)
renvoie une valeur oùb
est inclus ou exclusif. J'ai également modifié la description de Wikipedia afin que les gens ne suivent pas aveuglément le pseudocode et créent des bogues difficiles à détecter. Pour .Net,Random.Next(a,b)
renvoie un numéro exclusif deb
so sans plus tarder, voici comment il peut être implémenté en C # /. Net:Essayez ce code .
la source
i = list.Count - 1
, c'est-à-dire la dernière itération, vousrnd.Next(i, list.Count)
rendra i. Vous avez donc besoini < list.Count -1
comme condition de boucle. Eh bien, vous n'en avez pas `` besoin '', mais cela économise 1 itération;)Méthode d'extension pour IEnumerable:
la source
OrderBy
utilise une variante QuickSort pour trier les éléments par leurs clés (ostensiblement aléatoires). Les performances de QuickSort sont O (N log N) ; en revanche, un shuffle de Fisher-Yates est O (N) . Pour une collection de 75 éléments, ce n'est peut-être pas un gros problème, mais la différence deviendra prononcée pour les grandes collections.Random.Next()
peut produire une distribution de valeurs raisonnablement pseudo-aléatoire, mais cela ne garantit pas que les valeurs seront uniques. La probabilité de clés en double augmente (de façon non linéaire) avec N jusqu'à ce qu'elle atteigne la certitude lorsque N atteint 2 ^ 32 + 1. LeOrderBy
QuickSort est un type stable ; ainsi, si plusieurs éléments se voient attribuer la même valeur d'index pseudo-aléatoire, alors leur ordre dans la séquence de sortie sera le même que dans la séquence d'entrée; ainsi, un biais est introduit dans le "shuffle".L'idée est d'obtenir un objet anonyme avec un article et un ordre aléatoire, puis de réorganiser les articles en fonction de cet ordre et de renvoyer la valeur:
la source
la source
var listCopy = list.ToList()
pour éviter de sauter tous les éléments de la liste entrante? Je ne vois pas vraiment pourquoi vous voudriez faire muter ces listes pour les vider.EDITER C'est
RemoveAt
une faiblesse de ma version précédente. Cette solution permet de surmonter cela.Notez l'option
Random generator
, si l'implémentation du framework de baseRandom
n'est pas thread-safe ou cryptographiquement assez forte pour vos besoins, vous pouvez injecter votre implémentation dans l'opération.Une implémentation appropriée pour une implémentation cryptographique forte thread-safe
Random
peut être trouvée dans cette réponse.Voici une idée, étendre IList d'une manière (espérons-le) efficace.la source
GetNext
ouNext
?Vous pouvez y parvenir en utilisant cette méthode d'extension simple
et vous pouvez l'utiliser en procédant comme suit
la source
Random
instance de classe en dehors de la fonction en tant questatic
variable. Sinon, vous pourriez obtenir la même graine de randomisation du minuteur si elle est appelée rapidement.C'est ma méthode préférée de lecture aléatoire lorsqu'il est souhaitable de ne pas modifier l'original. C'est une variante de l' algorithme "inside-out" de Fisher – Yates qui fonctionne sur n'importe quelle séquence énumérable (la longueur de
source
n'a pas besoin d'être connue depuis le début).Cet algorithme peut également être implémenté en allouant une plage de
0
àlength - 1
et en épuisant aléatoirement les indices en échangeant l'index choisi au hasard avec le dernier index jusqu'à ce que tous les indices aient été choisis exactement une fois. Ce code ci-dessus accomplit exactement la même chose mais sans l'allocation supplémentaire. Ce qui est plutôt soigné.En ce qui concerne la
Random
classe, c'est un générateur de nombres à usage général (et si je dirigeais une loterie, j'envisagerais d'utiliser quelque chose de différent). Il s'appuie également sur une valeur de départ basée sur le temps par défaut. Un petit soulagement du problème consiste à amorcer laRandom
classe avec leRNGCryptoServiceProvider
ou vous pouvez utiliser leRNGCryptoServiceProvider
dans une méthode similaire à celle-ci (voir ci-dessous) pour générer des valeurs doubles à virgule flottante aléatoires uniformément choisies, mais le fonctionnement d'une loterie nécessite à peu près la compréhension du caractère aléatoire et de la nature de la source de hasard.Le but de générer un double aléatoire (entre 0 et 1 exclusivement) est d'utiliser pour évoluer vers une solution entière. Si vous devez choisir quelque chose dans une liste basée sur un double aléatoire,
x
ce sera toujours0 <= x && x < 1
simple.Prendre plaisir!
la source
Si cela ne vous dérange pas d'en utiliser deux
Lists
, c'est probablement la façon la plus simple de le faire, mais probablement pas la plus efficace ou imprévisible:la source
Si vous avez un nombre fixe (75), vous pouvez créer un tableau avec 75 éléments, puis énumérer votre liste, en déplaçant les éléments vers des positions aléatoires dans le tableau. Vous pouvez générer le mappage du numéro de liste à l'index du tableau à l'aide du shuffle Fisher-Yates .
la source
J'utilise habituellement:
la source
la source
Voici un Shuffler efficace qui retourne un tableau d'octets de valeurs mélangées. Il ne mélange jamais plus que nécessaire. Il peut être redémarré là où il s'était précédemment arrêté. Mon implémentation réelle (non illustrée) est un composant MEF qui permet un shuffler de remplacement spécifié par l'utilisateur.
"
la source
Voici un moyen sûr pour les threads:
la source
la source
Une simple modification de la réponse acceptée qui renvoie une nouvelle liste au lieu de travailler sur place, et accepte la plus générale
IEnumerable<T>
comme le font de nombreuses autres méthodes Linq.la source
J'ai trouvé une solution intéressante en ligne.
Courtoisie: https://improveandrepeat.com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/
var shuffled = myList.OrderBy (x => Guid.NewGuid ()). ToList ();
la source
Ancien poste à coup sûr, mais j'utilise juste un GUID.
Un GUID est toujours unique et puisqu'il est régénéré à chaque fois que le résultat change à chaque fois.
la source
Une approche très simple de ce type de problème consiste à utiliser un certain nombre de permutation d'éléments aléatoires dans la liste.
En pseudo-code, cela ressemblerait à ceci:
la source