【问题标题】:(Game of Life) How to loop through outer layer of a matrix without checking outside of it, simultaneously checking for neighbors?(生命游戏)如何遍历矩阵的外层而不检查它的外部,同时检查邻居?
【发布时间】:2018-06-18 11:19:09
【问题描述】:

矩阵中的每个点代表一个细胞,无论是活的还是死的。我必须计算每个细胞有多少活着的邻居。我有一个可以完成这项工作的函数,但它会检查其边界之外的单元格。我不知道如何在不执行大量 if-else 语句的情况下同时检查邻居并跟踪边缘。

void Neighbours(int rows, 
                int cols, cell world[rows][cols], 
                int neighbors[rows][cols]) {

//Loop through each cell in the matrix. 
for(int rCell = 0; rCell < rows; rCell++){
  for(int cCell = 0; cCell < cols; cCell++) {
    //Reset neighbor count for each cell.
    neighbors[rCell][cCell] = 0;
    //Check cell status in cell's vicinity of each cell. 
    for(int surroundR = -1; surroundR <= 1; surroundR++){
      for(int surroundC = -1; surroundC <= 1; surroundC ++) {
        //CONDITIONS
        //1. If the cell is alive,
        //2. if the cell is not itself,
        //3. if it does exist within the boundaries of the matrix.
        if(field[rCell - surroundR][cCell - surroundC].status == ALIVE) {
          if(!(surroundR  == 0 && surroundC  == 0) && (rCell-surroundR < rows) && (cCell-surroundC < cols) && (cCell-surroundC  >= 0) && (rCell-surroundR  >= 0)) {
            neighbors[rCell][cCell] += 1;
          }
        }
      }
    }
  } 
}
}

【问题讨论】:

  • 如何使用第一个和最后一个虚拟列和行,用一些非活动值初始化?如有必要,将非活动值添加到可能的值中,并进行特殊处理。
  • 这实际上是一道算法题,而不是一道 C 编程题。您正在寻找的是进行这些检查的最有效的方法,算法方面。实现“人生游戏”是一个经典的问题,所以肯定已经有人对此进行了思考。我建议在cs.stackexchange.com 提出这个问题。那么当你有了理想的理论算法后,你可以尝试用 C 来实现,然后在遇到实现问题时在这里询问。
  • 请将您的 C/C#/C++/Java 代码替换为等效的伪代码。
  • 我认为这是一个关于编程的问题,而不是算法。算法步骤是“检查每个邻居”,您在问如何有效地实现这一点。
  • @Lundin 作为 CS SE 的普通用户,我想说这绝对是题外话。该算法是“检查相邻的方块”,这里的问题是关于如何在编程语言语句级别实现该算法(具体来说,没有大量的ifs 和elses)。 CS SE 不做“我应该如何实现这个?”

标签: c matrix logic indexoutofboundsexception conways-game-of-life


【解决方案1】:

处理此问题的最简单方法是添加两个虚拟行和列:矩阵上方的一行、矩阵下方的一行、矩阵左侧的列和矩阵右侧的列。您一劳永逸地将它们设置为 DEAD,并仅在原始矩阵中的单元格上运行循环。

【讨论】:

    【解决方案2】:

    一种方法是在数组边缘用死单元的行和列填充数组。

    另一种方法是编写单独的代码:

    • 一个循环处理数组的内部单元格(省略边缘行和列),检查所有邻居。

    • 一个循环处理顶行,只检查顶行单元格的实际邻居。仅处理行内部的单元格,省略角。

    • 同样,底行有一个循环,左列有一个循环,右列有一个循环。

    • 四个角分别检查。

    虽然这是更多的代码,但它执行的检查少于对每个单元格进行一次循环检查。注意顶行和底行的循环可以合并(因为它们共享列坐标),左边缘和右边缘的循环可以合并。

    计算负载的额外减少是可能的。例如,主阵列中每个 3*1 条带中的活细胞计数可以被缓存(在辅助阵列中或仅在精心设计的循环中的变量中)以供重复使用。主阵列内部的每个这样的条带在计算各个单元周围的 3*3 方格时使用了 3 次,因此缓存它们的计数可以消除一些重复工作。

    (关于填充:要使用正确的 C 代码,您需要两列,一列在左侧,一列在右侧。但是,如果您的 C 实现支持越界访问子数组元素,只要寻址的元素在完整的数组,或者您手动将二维映射到一维数组中,那么一个填充列就足够了,因为它既位于正确的数组左边缘的左侧,也位于右边缘的右侧。)

    【讨论】:

      【解决方案3】:

      您可以轻松地出售一些内存以节省时间,方法是使用每侧包含一行且只有 dead 元素的数组,并且只在内部进行处理。算法变得更简单,错误风险更小,但整个代码必须更改为添加 2 行和 2 行:

      void Neighbours(int rows, 
                      int cols, cell world[rows][cols], 
                      int neighbors[rows][cols]) {
      
      //Loop through each cell in the matrix. 
      for(int rCell = 1; rCell < rows-1; rCell++){   // limit to the interior
        for(int cCell = 1; cCell < cols-1; cCell++) {
           // remaining part of the loops is unchanged
      

      【讨论】:

      • 这是一个非常有趣的解决方案。但是,我必须有 20x20。
      【解决方案4】:

      当您进行这种检查时 - 在索引到数组之前检查索引是否在您将要索引的数组的大小范围内。在索引到数组之前,您必须先执行此操作。早些时候,由于访问数组索引越界,您有未定义的行为。

      if( (rCell - surroundR) <= rows-1 && (cCell - surroundC)<= cols-1 && (rCell - surroundR)>=0 && (cCell - surroundC)>=0 ) {
         /* then do rest of work */
      
      }
      

      【讨论】:

      • 我可能有未定义的行为,但以下条件确保不会发生这种情况。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2019-04-09
      • 1970-01-01
      • 2018-07-03
      • 2021-12-18
      相关资源
      最近更新 更多