Il n'est pas une manière de brouiller que j'aime, principalement au motif que c'est O(n log n) pour aucune bonne raison quand il est facile à mettre en œuvre un O(n) shuffle. Le code de la question "œuvres" par essentiellement donné aléatoire (espérons-le unique!) nombre de chaque élément, puis de commander les éléments en fonction de ce nombre.
Je préfère Durstenfield de la variante de la shuffle de Fisher-Yates qui échange des éléments.
Mise en œuvre d'un simple, Shuffle
extension de la méthode se composent essentiellement d'appel ToList
ou ToArray
sur l'entrée à l'aide d'un existant, la mise en œuvre de Fisher-Yates. (Passer dans l' Random
comme paramètre pour rendre la vie généralement plus agréable.) Il y a beaucoup de mises en œuvre autour... j'ai probablement l'un dans une réponse quelque part.
La bonne chose à propos d'une telle extension de la méthode est qu'elle pourrait alors être très clair pour le lecteur de ce que vous êtes en train d'essayer de faire.
EDIT: Voici une mise en œuvre simple (pas de contrôle d'erreur!):
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
T[] elements = source.ToArray();
// Note i > 0 to avoid final pointless iteration
for (int i = elements.Length-1; i > 0; i--)
{
// Swap element "i" with a random earlier element it (or itself)
int swapIndex = rng.Next(i + 1);
T tmp = elements[i];
elements[i] = elements[swapIndex];
elements[swapIndex] = tmp;
}
// Lazily yield (avoiding aliasing issues etc)
foreach (T element in elements)
{
yield return element;
}
}
EDIT: Commentaires sur les performances ci-dessous m'a rappelé que nous pouvons retourner les éléments que nous avons shuffle:
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
T[] elements = source.ToArray();
for (int i = elements.Length - 1; i >= 0; i--)
{
// Swap element "i" with a random earlier element it (or itself)
// ... except we don't really need to swap it fully, as we can
// return it immediately, and afterwards it's irrelevant.
int swapIndex = rng.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
}
Ce sera désormais uniquement faire autant de travail que nécessaire.
Notez que dans les deux cas, vous devez être prudent au sujet de l'instance de Random
vous utilisez:
- La création de deux instances de l'
Random
environ à la même heure, produira la même séquence de nombres aléatoires (lorsqu'il est utilisé de la même manière)
-
Random
n'est pas thread-safe.
J'ai un article sur Random
qui va plus dans le détail sur ces questions et propose des solutions.