Best Way to Randomize an Array With .Net

Best way to randomize an array with .NET

If you're on .NET 3.5, you can use the following IEnumerable coolness:

Random rnd=new Random();
string[] MyRandomArray = MyArray.OrderBy(x => rnd.Next()).ToArray();

Edit: and here's the corresponding VB.NET code:

Dim rnd As New System.Random
Dim MyRandomArray = MyArray.OrderBy(Function() rnd.Next()).ToArray()

Second edit, in response to remarks that System.Random "isn't threadsafe" and "only suitable for toy apps" due to returning a time-based sequence: as used in my example, Random() is perfectly thread-safe, unless you're allowing the routine in which you randomize the array to be re-entered, in which case you'll need something like lock (MyRandomArray) anyway in order not to corrupt your data, which will protect rnd as well.

Also, it should be well-understood that System.Random as a source of entropy isn't very strong. As noted in the MSDN documentation, you should use something derived from System.Security.Cryptography.RandomNumberGenerator if you're doing anything security-related. For example:

using System.Security.Cryptography;

...

RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
string[] MyRandomArray = MyArray.OrderBy(x => GetNextInt32(rnd)).ToArray();

...

static int GetNextInt32(RNGCryptoServiceProvider rnd)
{
byte[] randomInt = new byte[4];
rnd.GetBytes(randomInt);
return Convert.ToInt32(randomInt[0]);
}

How to use Random class to shuffle array in C#

You can try implementing Fisher-Yates shuffling algorithm: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

    Random random = new Random();

...

for (int i = 0; i < array.Length - 1; ++i)
{
int r = random.Next(i, array.Length);
(array[r], array[i]) = (array[i], array[r]);
}

I suggest extracting a method for this:

// Easiest, but not thread safe
private static Random random = new Random();

private static void Shuffle<T>(T[] array)
{
if (array is null)
throw new ArgumentNullException(nameof(array));

for (int i = 0; i < array.Length - 1; ++i)
{
int r = random.Next(i, array.Length);
(array[r], array[i]) = (array[i], array[r]);
}
}

And you can call it from Main:

Shuffle(array);

You can fiddle with the algorithm.

Randomly rearrange array items c#

Using Random and Linq, you can do it easily:

Random r = new Random();

myArray = myArray.OrderBy(x => r.Next()).ToArray();

The above provides a random sort order for each element in the array using a Random object.

You need to add using System.Linq; at the top of your file in order to use OrderBy extension method.

Randomly shuffling an array

You can OrderBy(c => rnd.Next()) like this

Random rnd = new Random();
int[] arr = Enumerable.Range(0, 24).OrderBy(c => rnd.Next()).ToArray();

How to randomize/shuffle two arrays in the same way in c#

Just create two temporary variables

for (int i = 0; i < cards.Length - 1; i++)
{
int j = rnd.Next(i, cards.Length);

PictureBox tempPictureBox = cards[j]; // One for PictureBox
cards[j] = cards[i];
cards[i] = tempPictureBox;

int tempInt = numValue[j]; // Another one for int
numValue[j] = numValue[i];
numValue[i] = tempInt;
}

Is there a way to randomize through an array without repeating and then loop again cleanly?

I have tried to run this code in LINQpad, however there are other errors in this code. I have cleaned up the code below:

private string[] numbers = {
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10"
};

private string[] previousNumbers = { "", "", "", "", "", "", "", "", "", "" };

private int Randomize(int min, int max)
{
Random rnd = new Random();
return rnd.Next(min, max);
}

private string ReturnText()
{
int num = Randomize(0, 9);
string number = numbers[num];
previousNumbers.SetValue(number, num);
if (number == previousNumbers[num])
{
string numberToReturn = numbers[Randomize(0, 9)];
return numberToReturn;
}
else if (number != previousNumbers[num])
{
return number;
}
else
{
return string.Empty;
}
}

The types of errors in the code seem like this code hasn't been run in visual studio and it's syntax would cause a build failure:

  • Randomise return void when it returns an int.
  • Not all code paths return in ReturnText.
  • Missing semi-colons.

With fixed up code when I call ReturnText() over and over again I get no StackOverflowException.

Randomize a ListT

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


Related Topics



Leave a reply



Submit