【问题标题】:Optimizing Conway's Game of life优化康威的人生游戏
【发布时间】:2018-05-25 01:13:54
【问题描述】:

我正在努力加快康威的生命游戏。现在,代码查看一个单元格,然后将该点周围的 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. 非常感谢您提供的提示和需要考虑的事项。

谢谢大家。我已经为此工作了几个小时,不知道出了什么问题。看起来它应该可以工作,但我总是在董事会的随机位置得到不正确的解决方案。

【问题讨论】:

  • 您是在尝试优化工作代码还是修复损坏的代码?你不能同时做这两件事,这是没有意义的。
  • @RetiredNinja 他试图优化工作代码,结果产生了损坏的代码。
  • 看看几十年前迈克尔·阿巴拉什(Michael Abarash)对此的挑战,了解如何快速运行这种愚蠢的...

标签: c


【解决方案1】:

neighbors2 中,您设置i_lefti_right 以便将其限制在网格的行中。如果当前单元格在顶行或底行,则只循环两行而不是 3 行。

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

你不应该在i = 0时调用first_row(),也不应该在i == HEIGHT时调用third_row()

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 变量都是针对行的,从上到下,而不是从左到右。

【讨论】:

  • 我不确定行和列的方式,所以我没有改变它。我得到了这些变量(我同意,命名非常糟糕。应该是 x 和 y,顶部和底部)。不管怎样,我仍然得到一个不正确的解决方案。
  • 是的,这不起作用。我不知道从这里去哪里。
  • 您需要提供更多关于它如何不起作用的细节。
【解决方案2】:
#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 代码解决方案的两倍。

也不需要换板。感谢对细胞第二领域的投入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-07
    • 2012-01-19
    • 1970-01-01
    • 2018-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-21
    相关资源
    最近更新 更多