优化康威的生命游戏

时间:2018-05-25 01:13:54

标签: c

我正致力于加速康威的生命游戏。现在,代码查看一个单元格,然后在该点周围加上3x3区域,然后在我们正在查看的点处减去该值。以下是正在执行此操作的功能:

static int neighbors2 (board b, int i, int j)
{
    int n = 0;
    int i_left = max(0,i-1);
    int i_right  = min(HEIGHT, i+2);

    int j_left = max(0,j-1);
    int j_right  = min(WIDTH, j+2);
    int ii, jj;

    for (jj = j_left; jj < j_right; ++jj) {
        for (ii = i_left; ii < i_right; ii++) {
            n += b[ii][jj];
        }
    }

    return n - b[i][j];
}

以下是我一直试图用来迭代碎片的代码:

//Iterates through the first row of the 3x3 area
static int first_row(board b, int i, int j) {
    int f = 0;
    int i_left = max(0,i-1);

    int j_left = max(0,j-1);
    int j_right  = min(WIDTH, j+2);
    int jj;

    for (jj = j_left; jj < j_right; ++jj) {
        f += b[i_left][jj];
    }

    return f;
}

//Iterates and adds up the second row of the 3x3 area
static int second_row(board b, int i, int j) {
    int g = 0;
    int i_right  = min(HEIGHT, i+2);

    int j_left = max(0,j-1);
    int j_right  = min(WIDTH, j+2);
    int jj;

    if (i_right != i) {
        for (jj = j_left; jj < j_right; ++jj) {
            g += b[i][jj];
        }
    }

    return g;
}

//iterates and adds up the third row of the 3x3 area.
static int third_row(board b, int i, int j) {
    int h = 0;
    int i_right  = min(HEIGHT, i+2);

    int j_left = max(0,j-1);
    int j_right  = min(WIDTH, j+2);
    int jj;

    for (jj = j_left; jj < j_right; ++jj) {
        h += b[i_right][jj];
    }

    return h;
}

//adds up the surrounding spots
//subtracts the spot we're looking at.
static int addUp(board b, int i, int j) {
    int n = first_row(b, i, j) + second_row(b, i, j) + third_row(b, i, j);
    return n - b[i][j];
}

但是,由于某种原因,它不起作用。我不明白为什么。

需要注意的事项:

  1. 有时i == i_right,所以我们不想两次加一行。
  2. 这三个函数应该与单独的neighbors2完全相同。
  3. minmax是我预先制作的功能。
  4. 有时候有时j == j_right,所以我们不想两次加起来。我非常有信心循环可以解决这个问题。
  5. 需要考虑的提示和事项。
  6. 谢谢大家。我现在已经在这个工作了几个小时,不知道出了什么问题。它似乎应该可以工作,但我一直在董事会的随机位置得到不正确的解决方案。

2 个答案:

答案 0 :(得分:0)

neighbors2中,您设置i_lefti_right,以便将“#”限制为网格行。如果当前单元格位于顶行或底行,则只循环两行而不是3行。

first_row()last_row()中,您还将其限制为网格的行。但结果是这些函数会将单元格添加到与当前单元格相同的行上,这是second_row的作用。所以你最终将这些行添加两次。

first_row()时你不应该致电i = 0,而third_row()时你不应该致电i == HEIGHT

static int addUp(board b, int i, int j) {
    int n = (i == 0 ? 0 : first_row(b, i, j)) + 
            second_row(b, i, j) + 
            (i == HEIGHT ? 0 : third_row(b, i, j));
    return n - b[i][j];
}

另一个选择是检查函数本身:

function first_row((board b, int i, int j) {
    if (i == 0) {
        return 0;
    }
    int f = 0;
    int j_left = max(0,j-1);
    int j_right  = min(WIDTH, j+2);
    int jj;

    for (jj = j_left; jj < j_right; ++jj) {
        f += b[i][jj];
    }

    return f;
}    

,同样适用于third_row()。但是在调用者中执行此操作可以节省函数调用的开销。

顺便说一句,你的变量名称非常令人困惑。所有i变量都是行,从上到下,而不是从左到右。

答案 1 :(得分:0)

#include <stdio.h>
#include <stdlib.h>

#define ROWSDISP 50
#define COLSDISP 100

int rows=ROWSDISP+2, cols=COLSDISP+2;

这是为了避免在穿越邻居时出现非法索引。

struct onecell {char alive;
                char neibs;} **cells;

这是(动态)二维数组的小型结构的基础。

为每行创建空间,再加上容纳行指针数组的空间:

void init_cells()
{
    int i;
    cells = calloc(rows, sizeof(*cells));
    for(i=0; i<=rows-1; i++)
        cells[i] = calloc(cols, sizeof(**cells));
}

我跳过rand_fill()和glider()函数。可以设置一个单元格 cells[y][x].alive=1

int main(void) {

    struct onecell *c, *n1, *rlow;
    int i, j, loops=0;
    char nbs;

    init_cells();
    rand_fill();
    glider();

    while (loops++ < 1000) {
        printf("\n%d\n", loops);
        for (i = 1; i <= rows-2; i++) {

            for (j = 1; j <= cols-2; j++) {

                c  =  &cells[ i ][ j ];  
                n1 =  &cells[ i ][j+1];
                rlow = cells[i+1];

                nbs = c->neibs + n1->alive + rlow[ j ].alive
                                           + rlow[j+1].alive
                                           + rlow[j-1].alive;
                if(c->alive) {
                    printf("@");

                    n1->neibs++; 
                    rlow[ j ].neibs++;
                    rlow[j+1].neibs++;
                    rlow[j-1].neibs++;

                    if(nbs < 2 || nbs > 3)
                        c->alive = 0;
                } else {
                    printf(" ");
                    if(nbs == 3)
                        c->alive = 1;
                }

            c->neibs = 0;        // reset for next cycle
            }
        printf("\n");
        }
    }
    return(0);
}

这里没有迭代3x3正方形。在这8个邻居中 仅检查下游的4个;但同时 他们的柜台都升了。

具有100x100网格的基准:

# time ./a.out >/dev/null
real    0m0.084s
user    0m0.084s
sys     0m0.000s
# bc <<<100*100*1000/.084
119047619

这100M单元中的每一个都需要检查8个邻居,因此这接近CPU频率(每个周期1个邻居检查)。

这似乎是Rosetta代码解决方案的两倍。

也不需要切换电路板。感谢对单元第二个领域的投资。