0

everyone. So I was trying to practice C# today, but I was stuck in figuring out how I can use the Random class to simply shuffle an array

like this for example:

using System;
                    
    public class Program
    {
        public static void Main()
        {
            int[] arr = {1,2,3,4,5};
        
            Random rand = new Random();
        
            for(int i =0; i < arr.Length; i++){
                int shuffle = rand.Next(arr[i]);
                Console.WriteLine(arr[shuffle]);
            }
            
        }
    }

As you can see I tried to use this int shuffle = rand.Next(arr[i]); as a shuffler, but I guess it just duplicates some elements in the array.I'm still noob at this, thanks in advance for the response.

2
  • 1
    Random doesn’t guarantee that you not will get duplicate numbers, as it’s name says it’s random Commented Oct 9, 2021 at 4:53
  • 1
    You probably want to use the Fisher–Yates shuffle algorithm. Commented Oct 9, 2021 at 5:04

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This is nice. And I especially like how you are swapping the array elements (array[r], array[i]) = (array[i], array[r]);
0

There are a few solutions out there, but the one I had used it to create a random array and sort based on that

static class Program
{
    static void Main(string[] args)
    {
        var array = new[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

        Shuffle(ref array);

        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
        // Fri
        // Wed
        // Sat
        // Thu
        // Mon
        // Sun
        // Tue

    }

    static readonly Random rng = new Random();

    public static void Shuffle<T>(ref T[] array)
    {
        double[] key = new double[array.Length];
        for (int i = 0; i < key.Length; i++)
        {
            key[i] = rng.NextDouble();
        }

        Array.Sort(key, array);
    }
}

An alternative is to use the following 1 liner LINQ statement

    static void Main(string[] args)
    {
        var array = new[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

        var result = array.OrderBy((item) => rng.NextDouble()).ToArray();

     }

4 Comments

Why allocate a new array and why call NextDouble() raqther than just Next()? Both seem inefficient to me.
@Enigmativity - NextDouble() is needed for equal probabilities as they ara have flat probability distribution between [0,1). Unfortunately, you can't do that will next unless the range is int.MaxValue which is exactly what NextDouble() does.
A double uses 8 bytes and int 4. It's got to be more expensive to produce a random double - and Next() does return a random value to int.MaxValue.
@Enigmativity - you can choose what you want, but are the limits you are putting on .Next(). Also this seems like micro-optimization something not discussed on the question.
-1

This type of shuffling is going to require seeding the Randomn class to the size of the array. And with each pass in the for loop, it's going to swap the current number with a random other number in the array.

Here is a sample of what the shuffle can look like:

int[] arr = {1,2,3,4,5};

Random rand = new Random();

for(int i = 0; i < arr.Length; i++){
  int shuffle = rand.Next(arr.Length);
  int n = arr[i];
  arr.SetValue(arr[shuffle], i);
  arr.SetValue(n, shuffle);
}

Console.Write('[' + arr[0].ToString());
for(int i = 1; i < arr.Length; i++){
  Console.Write("," + arr[i]);
}
Console.Write("]");

12 Comments

You can use shorter code: Console.Write($"[{string.Join(", ", arr[i])}]"); to provide demo output
This algorithm introduces a bias. You must use Fisher-Yates to get it right. The value for shuffle must always be equal to or greater than the loop value (in this case i).
@Enigmativity that's news to me about seeded Random's inherent flaw. Thanks for sharing. The bias appears to be more prevalent in very large sets.. not this set of 5 we're dealing with here in this young lad's HW assignment. And the Yates algo does not shuffle enough for me. With each pass of the set, its pool of available numbers decreases until eventually n = set size and there is no randomness. The algo works, but its designed more for saving CPU on very large sets.
@JosephTroy - I think you've misunderstood what I was saying. Your algorithm is an incorrectly implemented Fisher-Yates - unless you correct it, it has the bias. You only need one pass of Fisher-Yates to completely randomize - there is no need for multiple passes. And finally, the bias of the Random class is astronomically miniscule compared to the bias introduced by the incorrect Fisher-Yates you have in your answer.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.