(生命游戏)如何循环遍历矩阵的外层而不检查它的外部,同时检查邻居?

矩阵中的每个点代表一个生命或死亡的细胞。 我必须计算每个单元格中有多少个ALIVE邻居。 我有一个function,但它检查边界外的单元格。我不知道如何在不执行大量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 = 0) && (rCell-surroundR >= 0)) { neighbors[rCell][cCell] += 1; } } } } } } } 

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

总是在进行这种检查时 – 在索引到数组之前检查索引是否在您要索引的数组的大小范围内。 在索引到数组之前,您必须先执行此操作。 之前由于访问数组索引超出范围,您有未定义的行为。

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

您可以轻松地销售一些内存以节省时间,方法是使用每边只包含一行且只有元素的数组,并仅在内部进行处理。 算法变得更简单,错误风险更小,但必须更改整个代码以添加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 

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

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

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

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

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

  • 四个角分开检查。

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

可以进一步减少计算负荷。 例如,主arrays中每个3 * 1条带中的活细胞计数可以被缓存(在辅助arrays中或简单地在精心制作的循环中的变量中)以便重复使用。 主arrays内部的每个这样的条带在计算各个单元周围的3 * 3方块时使用三次,因此缓存它们的计数可以消除一些重复的工作。

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