Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Randomize a List<T>

What is the best way to randomize the order of a generic list in C#? I've got a finite set of 75 numbers in a list I would like to assign a random order to, in order to draw them for a lottery type application.

like image 759
mirezus Avatar asked Nov 07 '08 19:11

mirezus


2 Answers

If we only need to shuffle items in a completely random order (just to mix the items in a list), I prefer this simple yet effective code that orders items by guid...

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList(); 

As people have pointed out in the comments, GUIDs are not guaranteed to be random, so we should be using a real random number generator instead:

private static Random rng = new Random(); ... var shuffledcards = cards.OrderBy(a => rng.Next()).ToList(); 
like image 39
user453230 Avatar answered Sep 19 '22 09:09

user453230


Shuffle any (I)List with an extension method based on the Fisher-Yates shuffle:

private static Random rng = new Random();    public static void Shuffle<T>(this IList<T> list)   {       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;       }   } 

Usage:

List<Product> products = GetProducts(); products.Shuffle(); 

The code above uses the much criticised System.Random method to select swap candidates. It's fast but not as random as it should be. If you need a better quality of randomness in your shuffles use the random number generator in System.Security.Cryptography like so:

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;     } } 

A simple comparison is available at this blog (WayBack Machine).

Edit: Since writing this answer a couple years back, many people have commented or written to me, to point out the big silly flaw in my comparison. They are of course right. There's nothing wrong with System.Random if it's used in the way it was intended. In my first example above, I instantiate the rng variable inside of the Shuffle method, which is asking for trouble if the method is going to be called repeatedly. Below is a fixed, full example based on a really useful comment received today from @weston here on SO.

Program.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;       }     }   } } 
like image 177
grenade Avatar answered Sep 22 '22 09:09

grenade