A while back I posted
an extension method for shuffling Lists in C# using the
Fisher Yates Shuffle. The algorithm is valid but my implementation relied on the default random number generator in .Net (System.Random) which is flawed since it generates a predictable sort of random.
Here's a correction that uses a more random sort of random from
System.Security.Cryptography:
- public static void Shuffle<T>(this IList<T> list)
- {
- var provider = new RNGCryptoServiceProvider();
- int n = list.Count;
- while (n > 1)
- {
- var box = new byte[1];
- do provider.GetBytes(box);
- while (!(box[0] < n * (Byte.MaxValue / n)));
- var k = (box[0] % n);
- n--;
- var value = list[k];
- list[k] = list[n];
- list[n] = value;
- }
- }
public static void Shuffle<T>(this IList<T> list)
{
var provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
var box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
var k = (box[0] % n);
n--;
var value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Here's a simple test you can run to demonstrate the difference in the algorithms:
- using System;
- using System.Collections.Generic;
- using System.Security.Cryptography;
-
- namespace AlphabetSoup
- {
- class Program
- {
- static void Main()
- {
- var alphabet = GetAlphabet();
- foreach (var c in alphabet)
- Console.Write(c);
- Console.Write('\n');
- Console.Write('\n');
-
- Console.WriteLine("System.Random:");
- for (int i = 0; i < 10; i++)
- {
- alphabet = GetAlphabet();
- alphabet.QuickShuffle();
- foreach (var c in alphabet)
- Console.Write(c);
- Console.Write('\n');
- }
- Console.Write('\n');
- Console.Write('\n');
-
- Console.WriteLine("System.Security.Cryptography:");
- for (int i = 0; i < 10; i++)
- {
- alphabet = GetAlphabet();
- alphabet.Shuffle();
- foreach (var c in alphabet)
- Console.Write(c);
- Console.Write('\n');
- }
- Console.Write('\n');
- Console.Write('\n');
-
- Console.ReadKey();
- }
-
- static List<char> GetAlphabet()
- {
- var alphabet = new List<char>();
- for (int c = 65; c < 91; c++)
- alphabet.Add((char)c);
- return alphabet;
- }
- }
- static class Ext
- {
- public static void Shuffle<T>(this IList<T> list)
- {
- var provider = new RNGCryptoServiceProvider();
- int n = list.Count;
- while (n > 1)
- {
- var box = new byte[1];
- do provider.GetBytes(box);
- while (!(box[0] < n * (Byte.MaxValue / n)));
- var k = (box[0] % n);
- n--;
- var value = list[k];
- list[k] = list[n];
- list[n] = value;
- }
- }
- public static void QuickShuffle<T>(this IList<T> list)
- {
- var rng = new Random();
- var n = list.Count;
- while (n > 1)
- {
- n--;
- var k = rng.Next(n + 1);
- var value = list[k];
- list[k] = list[n];
- list[n] = value;
- }
- }
- }
- }
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace AlphabetSoup
{
class Program
{
static void Main()
{
var alphabet = GetAlphabet();
foreach (var c in alphabet)
Console.Write(c);
Console.Write('\n');
Console.Write('\n');
Console.WriteLine("System.Random:");
for (int i = 0; i < 10; i++)
{
alphabet = GetAlphabet();
alphabet.QuickShuffle();
foreach (var c in alphabet)
Console.Write(c);
Console.Write('\n');
}
Console.Write('\n');
Console.Write('\n');
Console.WriteLine("System.Security.Cryptography:");
for (int i = 0; i < 10; i++)
{
alphabet = GetAlphabet();
alphabet.Shuffle();
foreach (var c in alphabet)
Console.Write(c);
Console.Write('\n');
}
Console.Write('\n');
Console.Write('\n');
Console.ReadKey();
}
static List<char> GetAlphabet()
{
var alphabet = new List<char>();
for (int c = 65; c < 91; c++)
alphabet.Add((char)c);
return alphabet;
}
}
static class Ext
{
public static void Shuffle<T>(this IList<T> list)
{
var provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
var box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
var k = (box[0] % n);
n--;
var value = list[k];
list[k] = list[n];
list[n] = value;
}
}
public static void QuickShuffle<T>(this IList<T> list)
{
var rng = new Random();
var n = list.Count;
while (n > 1)
{
n--;
var k = rng.Next(n + 1);
var value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}
Here's the output on my machine: