简体   繁体   English

优化康威的生命游戏

[英]Optimizing Conway's Game of life

I'm working on speeding up Conway's Game of Life.我正在努力加速康威的生命游戏。 Right now, the code looks at a cell and then adds up the 3x3 area immediately surrounding the point, then subtracts the value at the point we're looking at.现在,代码查看一个单元格,然后将紧邻该点的 3x3 区域相加,然后减去我们正在查看的点处的值。 Here's the function that is doing that:这是执行此操作的函数:

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];
}

And here is the code I've been trying to use to iterate through pieces at a time:这是我一直试图用来一次迭代各个部分的代码:

//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];
}

But, for some reason it isn't working.但是,由于某种原因,它不起作用。 I have no idea why.我不知道为什么。

Things to note:注意事项:

  1. sometimes i == i_right , so we do not want to add up a row twice.有时i == i_right ,所以我们不想将一行加起来两次。
  2. The three functions are supposed to do the exact same thing as neighbors2 in separate pieces.这三个函数应该在不同的部分中做与neighbors2完全相同的事情。
  3. min and max are functions that were premade for me. minmax是为我预制的函数。
  4. sometimes sometimes j == j_right , so we do not want to add up something twice.有时有时j == j_right ,所以我们不想将某些东西加起来两次。 I'm pretty confident the loop takes care of this however.但是,我非常有信心循环会解决这个问题。
  5. Tips and things to consider are appreciated.提示和要考虑的事情表示赞赏。

Thanks all.谢谢大家。 I've been working on this for a couple hours now and have no idea what is going wrong.我已经为此工作了几个小时,但不知道出了什么问题。 It seems like it should work but I keep getting incorrect solutions at random spots among the board.看起来它应该可以工作,但我一直在董事会中的随机位置得到不正确的解决方案。

In neighbors2 , you set i_left and i_right so that the're limited to the rows of the grid.neighbors2 ,您设置i_lefti_right以便将它们限制为网格的行。 If the current cell is in the top or bottom row, you only loop through two rows instead of 3.如果当前单元格在顶行或底行,则只能循环遍历两行而不是 3 行。

In first_row() and last_row() you also limit it to the rows of the grid.first_row()last_row()您还将其限制为网格的行。 But the result is that these functions will add the cells on the same row as the current cell, which is what second_row does.但结果是这些函数将添加与当前单元格在同一行的单元格,这就是second_row所做的。 So you end up adding those rows twice.所以你最终将这些行添加了两次。

You shouldn't call first_row() when i = 0 , and you shouldn't call third_row() when i == HEIGHT .你不应该叫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];
}

Another option would be to do the check in the functions themselves:另一种选择是在函数本身中进行检查:

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;
}    

and similarly for third_row() .third_row()类似。 But doing it in the caller saves the overhead of the function calls.但是在调用者中这样做可以节省函数调用的开销。

BTW, your variable names are very confusing.顺便说一句,你的变量名很混乱。 All the i variables are for rows, which go from top to bottom, not left to right.所有i变量都用于行,从上到下,而不是从左到右。

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

#define ROWSDISP 50
#define COLSDISP 100

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

This is to avoid illegal indexes when stepping over the neighbours.这是为了避免在跨越邻居时出现非法索引。

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

This is the foundation of a (dynamic) 2D-array, of a small struct.这是小型结构的(动态)二维数组的基础。

To create space for each row plus the space to hold an array of row pointers:为每一行创建空间加上保存行指针数组的空间:

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

I skip the rand_fill() and glider() funcs.我跳过了 rand_fill() 和 glider() 函数。 A cell can be set by cells[y][x].alive=1 .一个单元格可以通过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);
}

There is no iterating a 3x3 square here.这里没有迭代 3x3 正方形。 Of the 8 neighbours, only the 4 downstream ones are checked; 8个邻居中,只检查下游的4个; but at the same time their counters are raised.但与此同时,他们的柜台也被抬高了。

A benchmark with 100x100 grid:具有 100x100 网格的基准测试:

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

And each of these 100M cells needs to check 8 neighbours, so this is close to the CPU frequency (1 neighbour check per cycle).并且这 100M 单元中的每一个都需要检查 8 个邻居,因此这接近 CPU 频率(每个周期 1 个邻居检查)。

It seems twice as fast as the rosetta code solution.它似乎是 rosetta 代码解决方案的两倍。

There also is no need to switch the boards.也无需切换板。 Thanks to the investment in the second field of a cell.得益于对电池第二个领域的投资。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM