J'ai besoin de 'trier' de manière aléatoire une liste d'entiers (0-1999) de la manière la plus efficace possible. Des idées?
Actuellement, je fais quelque chose comme ça:
bool[] bIndexSet = new bool[iItemCount];
for (int iCurIndex = 0; iCurIndex < iItemCount; iCurIndex++)
{
int iSwapIndex = random.Next(iItemCount);
if (!bIndexSet[iSwapIndex] && iSwapIndex != iCurIndex)
{
int iTemp = values[iSwapIndex];
values[iSwapIndex] = values[iCurIndex];
values[iCurIndex] = values[iSwapIndex];
bIndexSet[iCurIndex] = true;
bIndexSet[iSwapIndex] = true;
}
}
Un bon algorithme de brassage à temps linéaire est le shuffle de Fisher-Yates .
L’un des problèmes que vous rencontrerez dans l’algorithme que vous proposez est qu’à la fin du shuffle, votre boucle passera beaucoup de temps à rechercher des éléments choisis au hasard qui n’ont pas encore été permutés. Cela peut prendre un temps indéterminé une fois le dernier élément à échanger.
De plus, il semble que votre algorithme ne se termine jamais s'il y a un nombre impair d'éléments à trier.
static Random random = new Random();
public static IEnumerable<T> RandomPermutation<T>(IEnumerable<T> sequence)
{
T[] retArray = sequence.ToArray();
for (int i = 0; i < retArray.Length - 1; i += 1)
{
int swapIndex = random.Next(i, retArray.Length);
if (swapIndex != i) {
T temp = retArray[i];
retArray[i] = retArray[swapIndex];
retArray[swapIndex] = temp;
}
}
return retArray;
}
modifié pour gérer des listes ou d'autres objets implémentant IEnumerable
Nous pouvons en faire une méthode d'extension pour obtenir un énumérateur aléatoire pour toute collection IList
class Program
{
static void Main(string[] args)
{
IList<int> l = new List<int>();
l.Add(7);
l.Add(11);
l.Add(13);
l.Add(17);
foreach (var i in l.AsRandom())
Console.WriteLine(i);
Console.ReadLine();
}
}
public static class MyExtensions
{
public static IEnumerable<T> AsRandom<T>(this IList<T> list)
{
int[] indexes = Enumerable.Range(0, list.Count).ToArray();
Random generator = new Random();
for (int i = 0; i < list.Count; ++i )
{
int position = generator.Next(i, list.Count);
yield return list[indexes[position]];
indexes[position] = indexes[i];
}
}
}
Cette opération utilise une lecture inversée de Fisher-Yates sur les index de la liste que nous souhaitons énumérer de manière aléatoire. Son un peu d'une taille de porc (l'allocation de 4 * list.Count bytes), mais s'exécute dans O (n).
Comme Greg l'a souligné, le meilleur { Fisher-Yates shuffle } serait la meilleure approche. Voici une implémentation de l'algorithme de Wikipedia:
public static void shuffle (int[] array)
{
Random rng = new Random(); // i.e., Java.util.Random.
int n = array.length; // The number of items left to shuffle (loop invariant).
while (n > 1)
{
int k = rng.nextInt(n); // 0 <= k < n.
n--; // n is now the last pertinent index;
int temp = array[n]; // swap array[n] with array[k] (does nothing if k == n).
array[n] = array[k];
array[k] = temp;
}
}
La mise en œuvre ci-dessus repose sur Random.nextInt (int) fournissant suffisamment aléatoire et non biaisé résultats
Je ne suis pas sûr du facteur d’efficacité, mais j’ai utilisé quelque chose de similaire au suivant si vous n’êtes pas opposé à l’utilisation de ArrayList:
private ArrayList ShuffleArrayList(ArrayList source)
{
ArrayList sortedList = new ArrayList();
Random generator = new Random();
while (source.Count > 0)
{
int position = generator.Next(source.Count);
sortedList.Add(source[position]);
source.RemoveAt(position);
}
return sortedList;
}
Avec cela, vous n'avez pas à vous soucier de la permutation intermédiaire.
Pour améliorer votre efficacité, vous pouvez conserver un ensemble de valeurs/indices qui ont été permutés plutôt qu'un booléen indiquant leur remplacement. Choisissez votre index d'échange aléatoire dans le pool restant. Lorsque le pool a la valeur 0 ou lorsque vous avez parcouru la liste initiale, vous avez terminé. Vous n'avez pas le potentiel d'essayer de sélectionner une valeur d'index de permutation aléatoire.
Lorsque vous effectuez un échange, retirez-les simplement de la piscine.
Pour la taille des données que vous consultez, ce n'est pas grave.
La réponse d'ICR est très rapide, mais les tableaux résultants ne sont pas distribués normalement. Si vous voulez une distribution normale, voici le code:
public static IEnumerable<T> RandomPermutation<T>(this IEnumerable<T> sequence, int start,int end)
{
T[] array = sequence as T[] ?? sequence.ToArray();
var result = new T[array.Length];
for (int i = 0; i < start; i++)
{
result[i] = array[i];
}
for (int i = end; i < array.Length; i++)
{
result[i] = array[i];
}
var sortArray=new List<KeyValuePair<double,T>>(array.Length-start-(array.Length-end));
lock (random)
{
for (int i = start; i < end; i++)
{
sortArray.Add(new KeyValuePair<double, T>(random.NextDouble(), array[i]));
}
}
sortArray.Sort((i,j)=>i.Key.CompareTo(j.Key));
for (int i = start; i < end; i++)
{
result[i] = sortArray[i - start].Value;
}
return result;
}
Notez que dans mes tests, cet algorithme était 6 fois plus lent que celui fourni par ICR, mais c’est la seule façon pour moi d’obtenir une distribution de résultat normale
itemList.OrderBy(x=>Guid.NewGuid()).Take(amount).ToList()
qu'en est-il de :
System.Array.Sort(arrayinstance, RandomizerMethod);
...
//any evoluated random class could do it !
private static readonly System.Random Randomizer = new System.Random();
private static int RandomizerMethod<T>(T x, T y)
where T : IComparable<T>
{
if (x.CompareTo(y) == 0)
return 0;
return Randomizer.Next().CompareTo(Randomizer.Next());
}
le tour est joué!
Quelque chose ne va pas comme ça?
var list = new[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
var random = new Random();
list.Sort((a,b)=>random.Next(-1,1));
Je suppose que les deux dernières lignes doivent être interchangées dans la réponse de Micah. Donc, le code pourrait ressembler à
public static void shuffle(int[] array) {
Random rng = new Random(); // i.e., Java.util.Random.
int n = array.Length; // The number of items left to shuffle (loop invariant).
while (n > 1) {
int k = rng.Next(n); // 0 <= k < n.
n--; // n is now the last pertinent index;
int temp = array[n]; // swap array[n] with array[k] (does nothing if k == n).
array[n] = array[k];
array[k] = temp;
}
}