Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I improve this C# randomising method?

Tags:

c#

sorting

random

I think I've settled on this as the most simple and unit-testable method for randomising a list, but would be interested to hear of any improvements.

public static IList<T> RandomiseList<T>(IList<T> list, int seed)
{
    Random random = new Random(seed);
    List<T> takeFrom = new List<T>(list);
    List<T> ret = new List<T>(takeFrom.Count);

    while (takeFrom.Count > 0)
    {
        int pos = random.Next(0, takeFrom.Count - 1);
        T item = takeFrom[pos];
        takeFrom.RemoveAt(pos);
        ret.Add(item);
    }

    return ret;
}
like image 720
Neil Barnwell Avatar asked Nov 27 '22 00:11

Neil Barnwell


2 Answers

You want a shuffle, and the best way to do that is the Fisher-Yates shuffle:

public static IList<T> Randomise<T>(IList<T> list, int seed) 
{
    Random rng = new Random(seed); 

    List<T> ret = new List<T>(list);      
    int n = ret.Length;            
    while (n > 1) 
    {
        n--;                         
        int k = rng.Next(n + 1);  
        // Simple swap of variables
        T tmp = list[k];
        ret[k] = ret[n];
        ret[n] = tmp;
    }
    return ret;
}
like image 105
Joel Coehoorn Avatar answered Dec 22 '22 10:12

Joel Coehoorn


I liked Dennis Palmers idea of returning a shuffled IEnumerable instead of shuffle the list in place, but using the RemoveAt method makes it slow. Here is an alternative without the RemoveAt method:

public static IEnumerable<T> Shuffle<T>(IEnumerable<T> list, int seed) {
  Random rnd = new Random(seed);
  List<T> items = new List<T>(list);
  for (int i = 0; i < items.Count; i++) {
    int pos = rnd.Next(i, items.Count);
    yield return items[pos];
    items[pos] = items[i];
  }
}

I thried this with 10000 integers, and it's about 30 times faster.

like image 21
Guffa Avatar answered Dec 22 '22 11:12

Guffa