Mélangez n'importe quel (I)List
avec une méthode d'extension basée sur le Le remaniement Fisher-Yates :
public static void Shuffle<T>(this IList<T> list)
{
Random rng = new Random();
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;
}
}
Utilisation :
List<Product> products = GetProducts();
products.Shuffle();
Le code ci-dessus utilise la méthode très critiquée System.Random pour sélectionner les candidats à l'échange. C'est rapide mais pas aussi aléatoire que cela devrait l'être. Si vous avez besoin d'une meilleure qualité d'aléatoire dans vos mélanges, utilisez le générateur de nombres aléatoires dans System.Security.Cryptography comme suit :
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;
}
}
Une comparaison simple est disponible à l'adresse suivante http://thegrenade.blogspot.com/2010/02/when-random-is-too-consistent.html
Edit : Depuis que j'ai écrit cette réponse il y a quelques années, de nombreuses personnes m'ont fait des commentaires ou m'ont écrit pour souligner le gros défaut stupide 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 façon dont il a été conçu. Dans mon premier exemple ci-dessus, j'instancie la variable rng à l'intérieur de la méthode Shuffle, ce qui pose des problèmes si la méthode est appelée à plusieurs reprises. Vous trouverez ci-dessous un exemple complet et corrigé basé sur un commentaire très utile reçu aujourd'hui de @weston ici sur SO.
Programme.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;
}
}
}
}
4 votes
Il y a un problème ouvert pour intégrer cette fonctionnalité à .NET : github.com/dotnet/corefx/issues/461
5 votes
Vous pourriez être intéressé par ce paquet NuGet qui contient des méthodes d'extension pour le brassage de IList<T> et IEnumerable<T> à l'aide de l'algorithme de Fisher-Yates mentionné ci-dessous
0 votes
@Natan fyi, ils l'ont tué.
0 votes
Il existe également des Sélectionner N éléments aléatoires dans une liste<T>. et Shuffle avec OrderBy vs. Fisher-Yates discussion.
0 votes
Peut-on avoir un ensemble infini de 75 numéros ;) ?
5 votes
@Natan ils ont fermé le dossier parce que quelqu'un "a travaillé sur de nombreux projets et développé de nombreuses bibliothèques et n'a jamais eu besoin d'une telle méthode" ça m'a énervé. Maintenant, nous devons enquêter nous-mêmes, rechercher les meilleures implémentations, perdre du temps pour simplement réinventer la roue.
3 votes
Est-ce que je vois bien ? Pas une seule réponse fonctionnelle valide après 10 ans ? Peut-être avons-nous besoin d'une autre prime pour une solution qui traite de la quantité d'entropie nécessaire, pour mélanger une liste de 75 numéros $log2(75 !) = 364$ et comment nous pouvons l'obtenir. Il faudrait réalimenter même un RNG cryptographiquement sûr avec 256 bits d'entropie au moins une fois pendant un mélange de type fisher-yates.
1 votes
Et si le codeur habituel ne peut pas résoudre ce problème, aurons-nous tous joué aux mêmes 0,01% de jeux de solitaire possibles depuis toujours ?