.NET
array randomization
programming
coding techniques
software development

Best way to randomize an array with .NET

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you need to randomize an array in .NET, the standard answer is the Fisher-Yates shuffle. It is fast, works in place, and gives each permutation the same probability when the random number generator is used correctly.

Why Fisher-Yates is the usual answer

A good shuffle algorithm must avoid bias. Some naïve approaches look random enough in casual testing but do not give all orderings an equal chance.

Fisher-Yates works by walking backward through the array and swapping each element with a randomly chosen earlier position, including itself.

csharp
1using System;
2
3public static class ShuffleHelper
4{
5    public static void Shuffle<T>(T[] array, Random random)
6    {
7        for (int i = array.Length - 1; i > 0; i--)
8        {
9            int j = random.Next(i + 1);
10            (array[i], array[j]) = (array[j], array[i]);
11        }
12    }
13}
14
15var values = new[] { 1, 2, 3, 4, 5 };
16ShuffleHelper.Shuffle(values, Random.Shared);
17Console.WriteLine(string.Join(", ", values));

This runs in O(n) time and uses O(1) extra space.

Why not use OrderBy with random keys

A common shortcut is:

csharp
var shuffled = values.OrderBy(_ => Random.Shared.Next()).ToArray();

This is convenient, but it is not the best general-purpose shuffle. It:

  • allocates a new sequence
  • sorts instead of swapping in place
  • can introduce bias through key collisions
  • is slower than Fisher-Yates for large arrays

It may be acceptable for tiny one-off data sets where simplicity matters more than quality, but it should not be the default recommendation.

Reuse the random number generator

Another important detail is how you create Random. If you construct a new Random repeatedly in quick succession, older .NET code could produce similar sequences because of time-based seeding.

This is why code like this is a poor habit:

csharp
var random = new Random();
ShuffleHelper.Shuffle(values, random);

once per tight call site.

A better pattern is to reuse one generator or use Random.Shared in modern .NET.

csharp
ShuffleHelper.Shuffle(values, Random.Shared);

That keeps the API simple and avoids accidental reseeding problems.

Return a new array when mutation is not wanted

Sometimes you want a shuffled copy while leaving the original untouched. In that case, clone first and then shuffle the copy.

csharp
1using System;
2
3public static T[] ShuffledCopy<T>(T[] source)
4{
5    T[] copy = (T[])source.Clone();
6
7    for (int i = copy.Length - 1; i > 0; i--)
8    {
9        int j = Random.Shared.Next(i + 1);
10        (copy[i], copy[j]) = (copy[j], copy[i]);
11    }
12
13    return copy;
14}
15
16var original = new[] { "A", "B", "C", "D" };
17var shuffled = ShuffledCopy(original);
18Console.WriteLine(string.Join(", ", original));
19Console.WriteLine(string.Join(", ", shuffled));

That makes the mutation behavior explicit.

Use a cryptographic generator when randomness quality matters more

For games, UI, and general random ordering, Random is usually fine. If the shuffle affects security, fairness, or anything adversarial, use a cryptographically strong generator instead.

csharp
1using System;
2using System.Security.Cryptography;
3
4public static void SecureShuffle<T>(T[] array)
5{
6    for (int i = array.Length - 1; i > 0; i--)
7    {
8        int j = RandomNumberGenerator.GetInt32(i + 1);
9        (array[i], array[j]) = (array[j], array[i]);
10    }
11}

The algorithm stays the same. Only the source of randomness changes.

LINQ convenience versus algorithmic correctness

It is tempting to prefer one-liners, especially in examples. But array randomization is one of those places where the algorithm matters. A short but biased shuffle is still wrong.

That is why Fisher-Yates remains the standard recommendation:

  • simple
  • efficient
  • unbiased when implemented correctly
  • easy to reuse for arrays and lists

Common Pitfalls

The biggest mistake is using a sort-based trick such as OrderBy(Guid.NewGuid()) or OrderBy(_ => random.Next()) as the default shuffle. Those approaches are slower and do not express the real algorithm clearly.

Another issue is creating a new Random repeatedly in hot code paths. That can reduce randomness quality and make results less independent than expected.

Developers also forget whether the method mutates the input. If callers expect the original array to remain unchanged, an in-place shuffle can create bugs far away from the shuffle itself.

Finally, general-purpose randomness and security-grade randomness are not the same problem. Pick Random or RandomNumberGenerator based on the actual requirements.

Summary

  • Fisher-Yates is the standard way to shuffle an array in .NET.
  • It runs in linear time and shuffles in place without extra allocations.
  • Reuse a random generator or use Random.Shared instead of reseeding constantly.
  • Clone the array first if you need a shuffled copy rather than mutation.
  • Use RandomNumberGenerator instead of Random when the shuffle must be cryptographically strong.

Course illustration
Course illustration

All Rights Reserved.