In this article, we will look at how we can efficiently randomize an array in C#. We’ll explore several ways of achieving that and then we’ll compare the performance of each approach.
Let’s start randomizing!
Initial Setup
Before we can start randomizing our arrays, we need a way to initialize them:
public static class ArrayFunctions { public static int[] GetOrderedArray(int numberOfElements) { var array = new int[numberOfElements]; for (int i = 0; i < numberOfElements; i++) { array[i] = i; } return array; } }
We define the ArrayFunctions
class with one method. The GetOrderedArray()
method is static
and generates an array of integers in ascending order, based on the specified length provided as the parameter. The generated array is returned as the result.
Randomize an Array in C# Using LINQ
LINQ is one of the most powerful tools we have at our disposal in C#. It provides us with various ways of manipulating data.
We often use it to query or filter datasets, but we can utilize it for randomizing as well. We can achieve that in several ways, all using the OrderBy
method.
Randomize an Array in C# Using GUID
Let’s create a method utilizing the Guid
class:
public static int[] RandomizeWithOrderByAndGuid(int[] array) => array.OrderBy(x => Guid.NewGuid()).ToArray();
We create a new static
RandomizeWithOrderByAndGuid()
method. It directly returns the input array ordered by Guid.NewGuid()
. As OrderBy()
returns an IOrderedEnumerable
, we use ToArray
method to convert the result back to an array.
The OrderBy()
method is used to sort data based on a specified key. By ordering based on Guid.NewGuid()
, which generates a unique ID for each element in the array, we effectively shuffle the array elements randomly.
Randomize an Array in C# Using the Random Class
Next, we will rely on the Random class:
public static int[] RandomizeWithOrderByAndRandom(int[] array) => array.OrderBy(x => Random.Shared.Next()).ToArray();
In our ArrayFunctions
class, we create the RandomizeWithOrderByAndRandom()
method. Again, we use the OrderBy()
method from LINQ to shuffle the elements of the array. This time we sort them based on randomly generated numbers using the Next
method of Random.Shared
. Finally, we convert the result to an array and return it.
Randomize an Array in C# Using the Fisher-Yates Algorithm
We can use the Fisher-Yates algorithm to randomly re-order the elements of an array:
public static int[] RandomizeWithFisherYates(int[] array) { int count = array.Length; while (count > 1) { int i = Random.Shared.Next(count--); (array[i], array[count]) = (array[count], array[i]); } return array; }
We create the RandomizeWithFisherYates()
method that takes an integer array as input. Inside the method, we initialize the count
variable with the length of the input array. In each iteration of a while
loop, a random index is generated, and the element at that index is swapped with the element pointed by the count
variable using a Tuple
. This process continues until count
becomes 1. Finally, we return the modified array.
This, however, won’t be a fair race. This approach works directly on, and returns, the input array where the OrderBy
methods return a copy of the array.
Let’s even the playing field and create a second version of the Fisher-Yates algorithm:
public static int[] RandomizeWithFisherYatesCopiedArray(int[] array) { int count = array.Length; var arrayCopy = new int[count]; Array.Copy(array, arrayCopy, count); while (count > 1) { int i = Random.Shared.Next(count--); (arrayCopy[i], arrayCopy[count]) = (arrayCopy[count], arrayCopy[i]); } return arrayCopy; }
It is almost identical to the original RandomizeWithFisherYates()
method with the exception that we use the Array.Copy()
method to copy the input array. In the rest of the method, we work with the arrayCopy
variable and return it once we are done.
Testing Array Randomization Logic
Once we have your different randomization methods, we need to make sure they work as intended:
public class ArrayFunctionsTests { private readonly int[] _array; private const int ARRAY_SIZE = 1000; public ArrayFunctionsTests() { _array = ArrayFunctions.GetOrderedArray(ARRAY_SIZE); } }
Using the xUnit framework, we create a test project and the ArrayFunctionsTests
class. It includes fields for an array and its size. In the constructor, we initialize the _array
by calling the GetOrderedArray()
method of our ArrayFunctions
class.
Then, we test our methods:
[Fact] public void WhenRandomizeWithOrderByAndGuidIsInvoked_ThenArrayIsRandomized() { // Act var randomizedArray = ArrayFunctions.RandomizeWithOrderByAndGuid(_array); // Assert randomizedArray .Should().NotBeNullOrEmpty() .And.NotBeInAscendingOrder() .And.NotBeInDescendingOrder(); }
Inside the test method, we use the RandomizeWithOrderByAndGuid()
method of the ArrayFunctions
class to randomize the ordered _array
variable. We store the result in the randomizedArray
variable. Then, using the FluentAssertions library, we assert that the randomizedArray
variable should not be null
or empty, should not be in ascending order, and should not be in descending order as well.
This way, we verify that the RandomizeWithOrderByAndGuid()
method successfully produces a different order than the original array and the array is not ordered in any direction.
To test the other three methods we have created in this article, we can use the same approach and change the method we invoke in our test.
Performance Considerations When Randomizing an Array in C#
When choosing a way to randomize an array in C# we also need to think about performance. We can use BenchmarkDotNet to measure performance and memory allocation:
| Method | Mean | Error | StdDev | Rank | Allocated | |------------------------------------ |----------:|----------:|----------:|-----:|----------:| | RandomizeWithFisherYates | 1.400 ms | 0.0069 ms | 0.0065 ms | 1 | 1 B | | RandomizeWithFisherYatesCopiedArray | 1.526 ms | 0.0025 ms | 0.0024 ms | 2 | 400057 B | | RandomizeWithOrderByAndRandom | 17.398 ms | 0.1137 ms | 0.1008 ms | 3 | 1602657 B | | RandomizeWithOrderByAndGuid | 25.921 ms | 0.0739 ms | 0.0617 ms | 4 | 2803288 B |
The benchmark we use measures the mean execution time, error, standard deviation, and memory allocation for each method.
RandomizeWithFisherYates()
method is the most efficient by far, with an execution time of about 1 400 microseconds. It ranks first and has almost no memory allocation at all.
Our modified version of the Fisher-Yates algorithm for evening the comparison, RandomizeWithFisherYatesCopiedArray()
ranks second, with 1 500 microseconds. It has more memory allocation than the original version due to copying the input array but overall performs extremely well.
Then, the RandomizeWithOrderByAndRandom()
method has an execution time of around 17 000 microseconds and significantly higher memory allocation compared to the previous two methods – it ranks third.
Finally, RandomizeWithOrderByAndGuid()
has an execution time of about 26 000 microseconds. It ranks last among the four methods and also has the highest memory allocation.
Conclusion
In this article, we explored different ways to randomize an array in C#. We examined how we can utilize LINQ with the Guid.NewGuid()
, or Random.Next()
methods, as well as implementing the Fisher-Yates algorithm.
When randomizing arrays in C#, it is important to choose an appropriate algorithm that balances randomness and performance according to the specific requirements of the application we are implementing. Using benchmarks to measure performance and memory usage, we discovered the superiority of the method using the Fisher-Yates algorithm.