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;
}
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With