Using Random class to randomize 2d array

Viewed 142

I am trying to randomize a sett of predetermine elements in a 2d array.

using System;

namespace array
{
    public class tiles
    {
        static void Main(string[] args)
        {
            Random random = new Random();
            int[,] tilearr = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } };

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    Console.Write(tilearr[i, j] + " ");
                }
                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

My problem is if I do something like tilearr[i, j] = random.Next(0, 8); it randomizes the array but doesn't care if there are any duplicates of the same element.

2 6 7
1 1 3
2 7 0

what I am after is more like this:

2 4 8  1 3 0
0 3 1  5 6 8 
6 7 5, 2 4 7
4 Answers

A simple and to the point solution would be to have a list of available numbers and then go position by position and randomly select the numbers out of the list.

Like this:

var numbers = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
for(int x = 0; x < 3; ++x) {
    for(int y = 0; y < 3; ++y) {
        // select a random number from the list ...
        int rand = random.Next(0, numbers.Count - 1);
        tilearr[x, y] = numbers[rand];
        // ... and remove it from the list
        numbers.RemoveAt(rand);
    }
}

As user Wai Ha Lee stated in the comments a shuffle will achieve what you are looking for. I would recommend the Fisher Yates Shuffle.

public static void Shuffle<T>(Random random, T[,] array)
{
    int lengthRow = array.GetLength(1);

    for (int i = array.Length - 1; i > 0; i--)
    {
        int i0 = i / lengthRow;
        int i1 = i % lengthRow;

        int j = random.Next(i + 1);
        int j0 = j / lengthRow;
        int j1 = j % lengthRow;

        T temp = array[i0, i1];
        array[i0, i1] = array[j0, j1];
        array[j0, j1] = temp;
     }
}

I retrieved this implementation from this answer.

This should be implemented in your code like this,

using System;

namespace array
{
    public class tiles
    {
        static void Main(string[] args)
        {
            Random random = new Random();
            int[,] tilearr = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } };
            Shuffle<int>(random, tilearr);

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    Console.Write(tilearr[i, j] + " ");
                }
                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

Make sure to place the shuffle generic function within your class.

HERE is an example of my implementation on dotnetfiddle.net.

One way to Randomize would be flatten the 2d array, shuffle it and then recreate based on original dimension. If you want to use Linq/Extension methods, you could do the following

Random random = new Random();
int[,] tilearr = {{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }};

var result = tilearr.OfType<int>()
       .OrderBy(x=> random.Next())
       .ChunkBy(tilearr.GetLength(1))
       .To2DArray();

Where ChunkBy and To2DArray are defined as

public static class Extensions
{
    public static IEnumerable<IEnumerable<T>> ChunkBy<T>(this IEnumerable<T> source, int chunkSize) 
    {
        return source
            .Select((x, i) => new { Index = i, Value = x })
            .GroupBy(x => x.Index / chunkSize)
            .Select(x => x.Select(v => v.Value));
    }

    public static T[,] To2DArray<T>(this IEnumerable<IEnumerable<T>> source)
    {
        var data = source
            .Select(x => x.ToArray())
            .ToArray();

        var res = new T[data.Length, data.Max(x => x.Length)];
        for (var i = 0; i < data.Length; ++i)
        {
            for (var j = 0; j < data[i].Length; ++j)
            {
                res[i,j] = data[i][j];
            }
        }

        return res;
    }
}

Sample Demo

If you generate the array from scratch, it's easier to randomize a one dimensional array, and then load it into a 2D array.

static int[,] GenerateArray(int size)
{
    Random r = new Random();   
    var arr = new int[size, size];
    var values = Enumerable.Range(0, size * size).OrderBy(x => r.Next()).ToArray();

    for (int i = 0; i < size; i++)
        for (int j = 0; j < size; j++)
            arr[i, j] = values[i * size + j];

    return arr;
}
Related