【问题标题】:C# Changing only part of an array, in random placesC# 仅在随机位置更改数组的一部分
【发布时间】:2011-11-30 18:46:56
【问题描述】:

我在控制台 c# 代码(7X3 'cells')中有一个 2D 整数数组。我创建它的那一刻,它被初始化为零。

我必须将只有五个“单元格”的值更改为 1,而不更改其他“单元格”的值。我知道这是一个基本的东西,但是,作为一个绝对的 C# 新手,无论我使用什么单元格,我都无法让它工作。你能帮助我吗?

提前致谢。

(注意:由于我是一个频繁的用户/贡献者,我知道这看起来像是一个经典的“做我的家庭作业”问题,我很抱歉,但因为我真的被困在这个小部分(以及我项目的其余部分)没关系),我会很感激帮助)。

【问题讨论】:

  • 请贴出创建、初始化和“更改值”数组的代码。
  • 同意上述观点。告诉我们实际的问题是什么。

标签: c# arrays random


【解决方案1】:

这里有两种解决这个问题的方法,第一种;名为 BruteForceRandomImplementation 的代码很容易实现和理解,但是一旦你尝试用 1 标记大量位置时速度就会非常慢,因为它像使用 Random 一样蛮力,直到它填满足够的位置。

    /// <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;
    }

使用以下辅助方法来保持我们的 locationsToFind 范围健全:

    /// <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);
    }

我的第二个实现称为ShuffleImplementation 在您标记大量独特位置时会很多更快。它创建一个一维数组,用足够的 1 填充它以满足您的需求,然后使用 Fisher-Yates shuffle 对该数组进行混洗,最终将这个一维数组扩展为二维数组:

    /// <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;
    }

用法:

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

或者:

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

取决于您要使用哪一个。有关两者之间性能差异的一个很好的例子,请尝试:

        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));

在我的笔记本电脑上,BruteForceRandomImplementation 花费了 1205 毫秒,而 ShuffleImplementation 花费了 67 毫秒或快了近 18 倍。

【讨论】:

  • @J. Kommer,我尝试过这样的事情,问题是,您的代码将整个数组更改为 1,而不仅仅是 5 个随机单元格。
  • 注意randomY和randomX的最大值分别是height-1width-1
  • 如果五次选择的随机数都相同怎么办?这不起作用。
  • @J. Kommer ,我在一台新电脑和一个新项目上再次尝试了它,它工作得很好。谢谢一百万,我真的很沮丧,因为它以前没有用。
  • 这行得通,不过我注意到如果数组中有一千个元素,并且要求您随机填写其中的 997 个,您可能会等待很长时间最后几个要填写的值。随着随机选择的元素数量变大,您的算法无法很好地扩展。
【解决方案2】:

所以你已经定义了一个多维数组(基本上是一个矩阵):

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

然后将所有值初始化为 0。

要改变一个值,你应该可以这样做,例如:

cells[3,2] = 1;

这是你尝试过的吗?您是否收到任何异常或警告?

如果你使用的是交错数组(arrays of arrays),那么它可能是这样的:

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

然后你可以设置值:

cells[3][2] = 1;

【讨论】:

    【解决方案3】:
    var array = new int[7,3];
    
    array[5,2] = 1;
    array[3,2] = 1;
    

    请记住,数组索引是从零开始的,因此 int[7,3] 的有效范围是 [0..6,0..2]

    【讨论】:

      【解决方案4】:
      int[,] cells = new int[7,3]; 
      cells[3,2] = 1; 
      

      【讨论】:

        猜你喜欢
        • 2017-06-27
        • 2012-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-08
        • 2019-03-01
        相关资源
        最近更新 更多