【问题标题】:How to check the surrounding cells of a specific cell in terms of a specific values in a rectangular array in C#如何根据 C# 中矩形数组中的特定值检查特定单元格的周围单元格
【发布时间】:2017-12-16 14:24:55
【问题描述】:

我使用以下方法根据给定索引(行和列)检查特定单元格周围的单元格是否为零,请参阅此数组:

我需要检查零周围的单元格(水平、垂直或对角线)可以位于第一行、最后一行、第一列、最后一列或介于两者之间,即它可以是矩形阵列中的任何单元格,具体取决于到传递给方法的“rowIndex”和“colIndex”。

static Boolean TestZero(int[,] array,int colIndex, int rowIndex)
{                 
    /*Check Corners*/
    //First Corner
    if ((rowIndex == 0) && (colIndex == 0)) 
    {

        if (array[1, 0] == 1 || array[0, 1] == 1 || array[1, 1] == 1) return false;


    }

    //Second Corner
    if ((rowIndex == 0) && colIndex >= array.GetUpperBound(0))
    {

        if (array[array.GetUpperBound(0) - 1, 0] == 1 || array[array.GetUpperBound(0),1] == 1 || array[array.GetUpperBound(0)-1,1 ] == 1) return false;


    }

    //Third Corner
    if ((rowIndex >= array.GetUpperBound(1)) && (colIndex == 0))
    {

        if (array[0, array.GetUpperBound(1) - 1] == 1 || array[1, array.GetUpperBound(1)] == 1 || array[1, array.GetUpperBound(1)-1] == 1) return false;


    }

    //Fourth Corner
    if ((rowIndex >= array.GetUpperBound(1)) && (colIndex >= array.GetUpperBound(0)))
    {

        if (array[array.GetUpperBound(0), array.GetUpperBound(1) - 1] == 1 || array[array.GetUpperBound(0) - 1, array.GetUpperBound(1) - 1] == 1 || array[array.GetUpperBound(0) -1, array.GetUpperBound(1)] == 1) return false;


    }

    /* Check Boundries But Not Corners */
    //First Row 
    if ((rowIndex == 0) && (colIndex != array.GetUpperBound(0)) && (colIndex != 0))
    {

        for (int i = rowIndex; i <= rowIndex + 1; i++)
        {
            for (int j = colIndex - 1; j <= colIndex + 1; j++)
            {
                if ((i != rowIndex) && (j != colIndex))
                {
                    if (array[j,i] == 1) return false;
                }
            }
        }


    }

    //Last Row 
    if ((rowIndex >= array.GetUpperBound(1)) && (colIndex != array.GetUpperBound(0)) && (colIndex != 0))
    {

        for (int i = rowIndex; i <= rowIndex - 1; i--)
        {
            for (int j = colIndex - 1; j <= colIndex + 1; j++)
            {
                if ((i != rowIndex) && (j != colIndex))
                {
                    if (array[j,i] == 1) return false;
                }
            }
        }


    }

    //First & Last Columns 
    if ((rowIndex != array.GetUpperBound(1)) && ((rowIndex != 0)))
    {  
        //First column
       if(colIndex==0) 
       {
        for (int i = rowIndex-1; i <= rowIndex + 1; i++)
        {
            for (int j = colIndex; j <= colIndex + 1; j++)
            {
                if ((i != rowIndex) && (j != colIndex))
                {
                    if (array[j,i] == 1) return false;
                }
            }
        }

       }

        //Last Column
       if (colIndex == array.GetUpperBound(0)) 
       {
           for (int i = rowIndex -1; i <= rowIndex + 1; i++)
        {
            for (int j = colIndex; j <= colIndex - 1; j--)
            {
                if ((i != rowIndex) && (j != colIndex))
                {
                    if (array[j,i] == 1) return false;
                }
            }
        }

       }

    }

    /* In Between i.e. Not the Array Boundries */
    if(colIndex!=0 && colIndex != array.GetUpperBound(0) && rowIndex !=0 && rowIndex != array.GetUpperBound(1)) {

        for (int i = rowIndex - 1; i <= rowIndex + 1; i++)
           {
             for (int j = colIndex - 1; j <= colIndex + 1; j++)
               {
                if ((i != rowIndex) && (j != colIndex))
                  {
                  if (array[j,i] == 1) return false;
                  }
               }
           }

     } // end if statment
    return true;
 }  

我得到了一些错误的结果,我试图找出问题所在,但我不能!。

结果:

1- 矩形阵列(遗传算法群体中的染色体):

2- 我们需要检查其周围环境的单元格的索引:

|(2,3)||(2,3)||(0,1)||(1,3)||(0,3)||(1,3)|

3- 在每个预期单元格的至少一个周围单元格中包含零的数组:

0 号染色体:真 1 号染色体:真 2 号染色体:假 3号染色体:真 4号染色体:假5号染色体:真

任何帮助弄清楚为什么我得到了一些错误的结果!!。

【问题讨论】:

  • 您是否尝试过在调试器中单步执行?除非你非常小心,否则这已经有点太复杂了,无法“用肉眼”分析。概括和简化逻辑会有所帮助(例如,创建一个像 GetSafe 这样的方法来进行边界检查,这样您就可以安全地进行 GetSafe(-1, 1) 而不是进行另一个显式的 if 检查)。它还允许您创建一个简单的相对位置数组来检查和循环,而不是执行if (GetSafe(...) &amp;&amp; GetSafe(...) &amp;&amp; GetSafe(...) &amp;&amp; ...) 或其他任何操作。
  • @Kushina,这是 ACM 问题吗?如果不是(或者如果您不关心性能),请使用 try-catch 处理边界情况,并使您的代码更易于调试。 (与@Luaan 类似的想法)
  • @Kushina,好的,看看我的答案,我现在要测试更多的案例。但请注意,它依赖于异常机制,这意味着性能不佳。

标签: c# arrays loops multidimensional-array


【解决方案1】:

据我所知,您的方法检查一个单元格是否被填充有1s 的单元格包围,如果是则返回false。您的代码对于它来说太复杂了,因为您尝试将所有内容视为不同的情况,而不是对其进行泛化,这使得调试变得非常困难。以下方法是实现检查的更好方法的示例:

    bool TestZero(int[,] mat, int row, int col)
    {
        int ones = 0, cells = 0;//define counters

        //define bounderies
        int rowLen = Math.Min(row + 1, mat.GetLength(0) - 1),
            colLen = Math.Min(col + 1, mat.GetLength(1) - 1),
            rowIdx = Math.Max(0, row - 1),
            colIdx = Math.Max(0, col - 1);
        for (int i = rowIdx; i <= rowLen; i++)
        {
            for (int j = colIdx; j <= colLen; j++)
            {
                //if it is our given index, continue
                if (i == row && j == col)
                    continue;
                ++cells;//increment cells counter
                if (mat[i, j] == 1)//if the value of the cell is 1
                    ++ones;//increment the ones counter
            }
        }

        return ones < cells;//if there are less cells with '1' then 
                            //surrounding cells, return true.
    }

我们在这里做的是:

创建两个计数器:一个计算给定单元格周围的单元格数量,另一个计算周围有多少个单元格。

我们将循环的边界保存在变量中:

  • rowLen:要访问的最后一行索引。它是给定单元格的行索引 + 1 和矩阵中的最后一行索引之间的较小值。
  • rowIdx:要在循环中检查的起始行索引。给定单元格的行索引 - 1 和矩阵中的第一行索引 (0) 之间的较大值。
  • colLen:与 rowLen 相同,仅用于列。
  • colIdx:与 rowIdx 相同,仅用于列。

然后我们迭代我们用我们的边界创建的迷你矩阵。对于每个单元格,如果不是我们给定的单元格,我们会增加单元格计数器,如果等于1,我们会增加个数计数器。

最后,如果个数计数器小于单元格计数器,我们返回真,因为我们的单元格没有被1s 包围。

编辑

如果不是所有周围的单元格都包含1,则上面的示例返回true。 但是可以更改返回值以匹配不同的情况:

  • 如果你想只在有 0 个带有1 的单元格时返回true,请将返回行更改为:return ones == 0; 在这种情况下,细胞计数器是不必要的,只需要个计数器。
  • 如果要在所有周围单元格都包含1时才返回true,请更改为:return ones == cells;

你基本上可以将返回值更改为你需要的任何情况,非常灵活。

【讨论】:

  • 该示例仅当所有周围单元格都包含'1'时才返回false。它可以很容易地更改为返回 true,但在这种情况下您的代码似乎返回 false,所以我选择了它,尽管它很奇怪。
  • 没有我的代码单独检查每种情况,如果在这种情况下被包围的单元格的值等于 1(至少一个单元格),则在每种情况下都返回 false,因此您需要修改您的答案..我需要测试一下...你可以吗?
  • 啊!哎呀,我混淆了 OR 和 AND。我编辑了答案以满足您的要求。就是对方法的返回值做一个简单的改动。
【解决方案2】:

与往常一样,编程时的第一条规则是:将问题分解成更小的部分

我将使用 C#7 功能只是为了好玩。如果您不使用 C#7,请考虑将其转换为以前的版本作为练习。

好的,第一步。你需要相邻的细胞吗?好吧,让我们获取所有可能的相邻单元格,而不关心它们是否存在。我们稍后会处理它;记住,一次一个小问题。

private static IEnumerable<(int Row, int Column)> GetAllNeighbouringCoordinates(int row, int column)
{
    yield return (row - 1, column - 1);
    yield return (row - 1, column);
    yield return (row - 1, column + 1);
    yield return (row, column + 1);
    yield return (row + 1, column + 1);
    yield return (row + 1, column);
    yield return (row + 1, column - 1);
    yield return (row, column - 1);
}

好的,现在我们有一个方法可以给我们所有可能的 8 个邻居。我返回它们的顺序是顺时针,从左上角的邻居开始。在这种情况下,顺序并不重要,因此请将其视为实现细节。

现在,我们需要某种方式来检查任何给定的单元格是否有效。好的,这似乎也很容易:

private static bool IsValidCoordinate((int Row, int Column) coord, int rowCount, int columnCount)
{
    Debug.Assert(rowCount >= 0);
    Debug.Assert(columnCount >= 0);

    if (0 > coord.Row || coord.Row >= rowCount ||
        0 > coord.Column || coord.Column >= columnCount)
        return false;

    return true;
}

好的,这也很简单。看看在简单方法中引入错误有多难?

另外,请注意方法开头的断言。此方法不适用于 rowCountcolumnCount 的无意义值,因此我在代码中强制。因为该方法是一个私有辅助方法,所以我可以简单地断言而不抛出异常。如果断言在测试中失败,我知道我的代码中有错误。

现在,我们只需要将两者粘合在一起。让我们构建一个返回所有相邻单元格值的方法。我们将使用一些 LINQ 来删除难看的循环:

public static IEnumerable<T> GetNeighbouringCells<T>((int Row, int Column) coord, T[,] cells)
{
    if (cells == null)
        throw new ArgumentOutOfRangeException();

    if (!IsValidCoordinate(coord, cells.GetLength(0), cells.GetLength(1)))
        throw new ArgumentOutOfRangeException();

    return GetAllNeighbouringCoordinates(coord.Row, coord.Column)
        .Where(c => IsValidCoordinate(c, cells.GetLength(0), cells.GetLength(1)))
        .Select(c => cells[c.Row, c.Column]);
}

你去吧,现在你有一个简单的方法可以返回任何给定单元格的每个相邻值。

现在,您需要至少一个相邻单元格为零的所有单元格吗?轻松愉快:

public static IEnumerable<(int Row, int Column)> CellsWithAtLeastOneNeighbourEqualTo<T>(
    this T[,] cells, T value)
{
    for (var row = 0; row < cells.GetLength(0); row++)
    {
        for (var column = 0; column < cells.GetLength(1); column++)
        {
            if (GetNeighbouringCells((row, column), cells).Any(c => c.Equals(value)))
            {
                yield return (row, column);
            }
        }
    }
}

现在,如果你带它去兜风:

var cells = new[,] { { 0, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } };
var n = cells.CellsWithAtLeastOneNeighbourEqualTo(0).ToList();

你会得到预期的结果:

[0, 1]
[1, 0]
[1, 1]

【讨论】:

    【解决方案3】:

    这似乎是一个简单的生命游戏程序。您不应该单独检查所有内容,而是使用例如函数来检查给定单元格的 x 和 y 坐标是否存在于表格中。

    伪代码:

    for each cell in celltable
        for each cell surrounding
            if cell index is valid && alive
                alive = alive + 1;
            endif
        endfor
        if alive is valid
           add cell to retList
        alive = 0;
    endfor
    

    没有人愿意调试庞大的 if-else 系统。

    【讨论】:

    • 这是一个非常简单的程序,可以使用任何常见的编程语言进行编码。我宁愿让你做这项工作。 :)
    【解决方案4】:

    这在性能上并不好,但可能会解决您的问题。

    附言。请注意,我将您的 colIndexrowIndex 重命名为 xy

    static bool TestZero(int[,] array, int x, int y)
        {
            try
            {
                if (array[x - 1, y - 1] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x, y - 1] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x + 1, y - 1] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x - 1, y] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x, y] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x + 1, y] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x - 1, y + 1] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x, y + 1] == 1) return false;
            }
            catch { }
            try
            {
                if (array[x + 1, y + 1] == 1) return false;
            }
            catch { }
            return true;
        }
    

    我使用您的原始案例(您帖子中的第一张图片)进行测试,使用以下代码。

    private static int[,] array = { { 0, 0, 1, 1 }, { 0, 0, 0, 1 }, { 0, 1, 1, 1 }, { 1, 0, 1, 0 } };
    
        static void Main(string[] args)
        {
            for (int i = 0; i <= array.GetUpperBound(0); i++)
            {
                for (int j = 0; j <= array.GetUpperBound(1); j++)
                {
                    Console.Write(TestZero(array, i, j) + " ");
                }
                Console.WriteLine();
            }
            Console.ReadKey();
        }
    

    结果是

    True False False False
    False False False False
    False False False False
    False False False False
    

    我会测试更多的案例,但你终于可以试一试了。

    【讨论】:

    • 顺便说一句,我的代码只回答“检查特定单元格周围的单元格”,如果你正在做一个现场游戏(或类似的东西),你需要做其他事情自己。
    • @Kushina,我已经尝试了大约 10 种不同数组大小(例如 4x6)的案例,到目前为止都通过了。如果您有任何问题,请发表评论并标记我。至于“UGLY”,如果你不在乎跑步性能,你可以忽略它。
    • 这简直太可怕了,太可怕了,太可怕了。啊!如果可能的话,我会否决它 10 次。使用异常作为正常的控制流机制是最糟糕的。这怎么可能是公认的答案,我无法理解。
    • @InBetween,是的,我完全同意。但我最初的想法是“避免复杂的边界问题”,以便 OP 轻松调试:P
    • @Kushina 仅仅因为此代码返回正确的值并不意味着它是正确的方法。简单地说,这个解决方案是一种行走的编码恐怖。您应该从不、从不、从不将异常用作控制流机制。异常是极其昂贵的,如果这段代码必须以任何远程执行的方式运行,你会大吃一惊。
    猜你喜欢
    • 1970-01-01
    • 2017-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多