jonatr
jonatr

Reputation: 380

C# Changing only part of an array, in random places

I've got a 2D array of int's in a console c# code (7X3 'cells'). The moment I create it, it is initialized with zeros.

I've got to change the value of only five 'cells' into 1, without changing the value of other 'cells'. I know it is a basic thing, but, as an absolute rookie in C#, I can't get it to work, no matter what cell I use. Can you help me?

Thanks in advance.

(Note: As I am a frequent user/contributer, I know it looks like a classic "Do my homework" question and I apologize, but as I'm really stuck on this weenie part (and the rest of my project is OK), I'd appreciate the help).

Upvotes: 2

Views: 618

Answers (4)

Johannes Kommer
Johannes Kommer

Reputation: 6451

Here are two ways of solving this issue, the first one; called BruteForceRandomImplementation is easy to implement and understand but quite slow once you attempt to mark a very large number of locations with 1s due to it's brute-force like usage of Random till it fills enough locations.

    /// <summary>
    /// This method uses a random based algorithm to create a two-dimensional array of [height, width] 
    /// with exactly locationsToFind locations set to 1. 
    /// </summary>
    /// <param name="height"></param>
    /// <param name="width"></param>
    /// <param name="locationsToFind"></param>
    /// <returns></returns>
    public int[,] BruteForceRandomImplementation(int height, int width, int locationsToFind)
    {
        var random = new Random();
        locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);

        // Create our two-dimensional array.
        var map = new int[height, width];

        int locationsFound = 0;

        // Randomly set positons to 1 untill we have set locationsToFind locations to 1. 
        while (locationsFound < locationsToFind)
        {
            // Get a random Y location - limit the max value to our height - 1. 
            var randomY = random.Next(height);
            // Get a random X location - limit the max value to our width - 1. 
            var randomX = random.Next(width);

            // Find another random location if this location is already set to 1. 
            if (map[randomY, randomX] == 1)
                continue;

            // Otherwise set our location to 1 and increment the number of locations we've found.
            map[randomY, randomX] = 1;
            locationsFound += 1;
        }

        return map;
    }

With the following helper method to keep our locationsToFind range sane:

    /// <summary>
    /// Limits the locationsToFind variable to the maximum available locations. This avoids attempting to 
    /// mark more locations than are available for our width and height. 
    /// </summary>
    /// <param name="height"></param>
    /// <param name="width"></param>
    /// <param name="locationsToFind"></param>
    /// <returns></returns>
    public int LimitLocationsToFindToMaxLocations(int height, int width, int locationsToFind)
    {
        return Math.Min(locationsToFind, height * width);
    }

My second implementation called ShuffleImplementation is a lot faster when you are marking large numbers of unique locations. It creates a one-dimensional array, fills this with enough 1s to satisify your needs then shuffles this array using the Fisher-Yates shuffle to finally proceed to expand this one-dimensional array into a two-dimensional one:

    /// <summary>
    /// This method uses a shuffle based algorithm to create a two-dimensional array of [height, width] 
    /// with exactly locationsToFind locations set to 1. 
    /// </summary>
    /// <param name="height"></param>
    /// <param name="width"></param>
    /// <param name="locationsToFind"></param>
    /// <returns></returns>
    public int[,] ShuffleImplementation(int height, int width, int locationsToFind)
    {
        locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);
        // Create a 1 dimensional array large enough to contain all our values. 
        var array = new int[height * width];

        // Set locationToFind locations to 1. 
        for (int location = 0; location < locationsToFind; location++)
            array[location] = 1;

        // Shuffle our array.
        Shuffle(array);

        // Now let's create our two-dimensional array.
        var map = new int[height, width];

        int index = 0;

        // Expand our one-dimensional array into a two-dimensional one. 
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                map[y, x] = array[index];
                index++;
            }
        }

        return map;
    }

    /// <summary>
    /// Shuffles a one-dimensional array using the Fisher-Yates shuffle. 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="arr"></param>
    public static void Shuffle<T>(T[] array)
    {
        var random = new Random();

        for (int i = array.Length - 1; i > 0; i--)
        {
            int n = random.Next(i + 1);
            Swap(ref array[i], ref array[n]);
        }
    }

    /// <summary>
    /// Swaps two values around. 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="valueA"></param>
    /// <param name="valueB"></param>
    public static void Swap<T>(ref T valueA, ref T valueB)
    {
        T tempValue = valueA;
        valueA = valueB;
        valueB = tempValue;
    }

Usage:

var map = BruteForceRandomImplementation(7, 3, 5);

Or:

var map = ShuffleImplementation(7, 3, 5);

Depending on which one you want to use. For a good example of the performance difference between the two, try:

        int width = 1000;
        int height = 1000;
        int locationsToFind = (width * height) - 1;

        var stopwatch = new Stopwatch();

        stopwatch.Start();
        BruteForceRandomImplementation(height, width, locationsToFind);
        stopwatch.Stop();

        Console.WriteLine(string.Format("BruteForceRandomImplementation took {0}ms", stopwatch.ElapsedMilliseconds));

        stopwatch.Restart();
        ShuffleImplementation(height, width, locationsToFind);
        stopwatch.Stop();

        Console.WriteLine(string.Format("ShuffleImplementation took {0}ms", stopwatch.ElapsedMilliseconds));

On my laptop BruteForceRandomImplementation took 1205ms and ShuffleImplementation took 67ms or nearly 18x faster.

Upvotes: 8

Cᴏʀʏ
Cᴏʀʏ

Reputation: 107536

So you've defined a multi-dimensional array (basically a matrix):

int[,] cells = new int[7,3];

And then you initialize all of the values to 0.

To change a value, you should be able to do it like this, for example:

cells[3,2] = 1;

Is that what you've tried? Are you receiving any exceptions or warnings?

If you're using jagged arrays (arrays of arrays), then it might be like:

int[][] cells = new int[7][3];

And then you can set values:

cells[3][2] = 1;

Upvotes: 3

dthorpe
dthorpe

Reputation: 36082

var array = new int[7,3];

array[5,2] = 1;
array[3,2] = 1;

Keep in mind that the array indices are zero-based, so the valid range of int[7,3] is [0..6,0..2]

Upvotes: 1

phoog
phoog

Reputation: 43046

int[,] cells = new int[7,3]; 
cells[3,2] = 1; 

Upvotes: 0

Related Questions