c - 使用边界条件导航映射到 1D 的 2D 数组

标签 c arrays conways-game-of-life sub-array

我正在尝试着重于效率以及模式匹配功能来实现生活游戏。其中图案是信号灯、滑翔机、十字等。

我有一个用于世界的一维数组,以及宽度和高度。为了找到邻居,我想计算摩尔邻域的索引,然后检查这些是否是散列,如果是,这会增加 get_neighbours 函数的返回变量。北方和南方似乎有效,但东方和西方却不行。 NE、SE、SW、NW 都是基于之前的逻辑(即西向北)。

int get_neighbours(int loc) {
    int neighbours = 0;

    int n = mod(loc - grid_width, total);
    int e = mod(loc + 1, grid_width) + grid_width;
    int s = mod(loc + grid_width, total);
    int w = mod(loc - 1, grid_width) + grid_width;
    int nw = mod(w - grid_width, total);
    int ne = mod(e - grid_width, total);
    int se = mod(e + grid_width, total);
    int sw = mod(w + grid_width, total);

    //Northwest
    if (grid[nw] == '#') {
        neighbours++;
    }
    //North
    if (grid[n] == '#') {
        neighbours++;
    }
    //Northeast
    if (grid[ne] == '#') {
        neighbours++;
    }
    //East
    if (grid[e] == '#') {
        neighbours++;
    }
    //Southeast
    if (grid[se] == '#') {
        neighbours++;
    }
    //South
    if (grid[s] == '#') {
        neighbours++;
    }
    //Southwest
    if (grid[sw] == '#') {
        neighbours++;
    }
    //West
    if (grid[w] == '#') {
        neighbours++;
    }
    return neighbours;
}

int mod(int a, int b) {
    int ret = a % b;
    if (b < 0) {
        return mod(-a, -b);
    }
    else if (ret < 0) {
        ret += b;
    }
    return ret;
}

对于模式匹配,我尝试使用与上述相同的逻辑来构建一个 5x5 子数组。这实质上使用了一个“读头”。从提供的位置向东穿过世界,直到它移动了 5 个空间。然后,它返回到原始位置并向南移动正确的行数,然后再次向东移动,直到我们收集到 25 个索引。

char *get_subarray(int loc) {
    char *subarray;
    subarray = malloc(sizeof(char) * 25);

    int i = 0;
    int ptr = loc;

    while (i < 25) {
        subarray[i] = grid[ptr];
        if ((i + 1) % 5 == 0) {
            //return the read head to the original location, then travel south through the grid once for each of the times we have traversed a row
            ptr = loc;
            for (int k = 0; k <= (i / 5); k++) {
                ptr = mod(ptr + grid_width, total);
            }
        } else {
            ptr = mod(ptr + 1, grid_width) + grid_width;
        }
        i++;
    }
    subarray[i] = '\0';
    return subarray;

}

当它这样做时,它从世界构建子数组,然后我可以针对模式字符串对它进行 strcmp()。

int cpu_get_crosses() {
    int crosses = 0;

    for (int i = 0; i < total; i++) {
        if (strcmp(get_subarray(i), "       #   # #   #       ") == 0) {
            crosses++;
        }
    }
    return crosses;
}

作为引用,带有索引(带有边界)的 7x5 网格:

34|28 29 30 31 32 33 34|28
--|--------------------|--
6 |0  1  2  3  4  5  6 |0
13|7  8  9  10 11 12 13|7 
20|14 15 16 17 18 19 20|14
27|21 22 23 24 25 26 27|21
34|28 29 30 31 32 33 34|28
--|--------------------|--
6 |0  1  2  3  4  5  6 |0

我很好奇什么逻辑允许我在保留边界条件的同时计算摩尔邻域的索引,以便我可以正确计算邻居和子数组(因为它们都使用相同的逻辑)。

编辑: 子数组函数,如果任何 googlers 需要的话。

char *get_subarray(int loc) {
    char *subarray;
    subarray = malloc(sizeof(char) * 25); //5x5 (=25) subarray

    int i = 0;
    int row = loc / grid_width;
    int ptr = loc;

    while (i < 25) {
        subarray[i] = grid[ptr];
        if ((i + 1) % 5 == 0) {
            //return the read head to the original location, then travel south through the grid once for each of the times we have traversed a row
            ptr = loc;
            for (int k = 0; k <= (i / 5); k++) {
                ptr = mod(ptr + grid_width, total);
            }
            row = ptr / grid_width;
        } else {
            ptr = mod(ptr + 1, grid_width) + row * grid_width;
        }
        i++;
    }
    subarray[i] = '\0';
    return subarray;
}

最佳答案

您正在以行方式索引数组:index(i, j) = j * grid_width + i for i=0..grid_width-1, j= 0..grid_height-1。让我们调用 loc index(i, j) 的结果并反转 index 得到 i j:

int i = loc % grid_width;
int j = loc / grid_width;

向东增加i一,向西减少一,两者都以宽度为模:

int e = j * grid_width + (i + 1) % grid_width
      = j * grid_width + ((j * grid_width + i) + 1) % grid_width
      = j * grid_width + (loc + 1) % grid_width;
int w = j * grid_width + (i + grid_width - 1) % grid_width
      = j * grid_width + ((j * grid_width + i) + grid_width - 1) % grid_width
      = j * grid_width + (loc + grid_width - 1) % grid_width;

注意:

  1. (i + grid_width - 1) % grid_width 等于 mod(i - 1, grid_width)
  2. x % M = (k * M + x) % M 对于任何整数 k,这让我们替换任何表达式中的 igrid_widthloc = j * grid_width + i 以避免首先计算 i ;)

j 增加一个模高度等于添加 grid_width 并用 total 换行,因为 total 是宽度 x 高度。更明确地说,这是推导:

int j1 = (j + 1) % grid_height;
int s = j1 * grid_width + i
      = ((j + 1) % grid_height) * grid_width + i
      = ((j + 1) * grid_width) % (grid_height * grid_width) + i
      = ((j + 1) * grid_width) % total + i
      = (j * grid_width + grid_width + i) % total
      = ((j * grid_width + i) + grid_width) % total
      = (loc + grid_width) % total;
// analogue for j0 = (j + grid_height - 1) % grid_height;
int n = (loc + total - grid_width) % total;

关于c - 使用边界条件导航映射到 1D 的 2D 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37251156/

相关文章:

C 编程 while 循环不要求输入

c - math.h 与文件的链接

c - 如何从一个 C 程序中调用两个 C 程序?

arrays - 使用 Firebase 控制台在 Firebase 中设置数组

javascript - 从加权类别中选择唯一字符串的最有效方法

c++ - C 等同于 C++ get_temporary_buffer?

c++ - 什么可能导致在 C++ 中指针的另一端丢失对象?

python - PyGame 中康威的生命游戏没有生成正确的模式

c - 在这个用 C 语言编写的生命游戏程序中,我无法在数组周围建立边界

c - 如何将许多 if 语句压缩成更小、更易读的内容?