Search code examples
arraysalgorithmiterationconways-game-of-life

Non-iterative algorithm for 1D game of life


Consider a Boolean array a[n], where each element is a cell. A cell becomes alive (set to true) in the next generation if one and only one adjacent cell is alive, otherwise it becomes dead (set to false). The first and last cell are considered neighbours.

Given a[n], the array size n, and a positive integer t, I wish to compute the a[n] after t'th generation of evolution, but without using any iterative algorithm on t, which can be potentially very large.

What I have observed: if we define S_k(a[n]) be a circular shift of a[n] to the right by k elements. That is, a[0] becomes a[k] after one shift if 0 <= k < n. Define a[n] ^ b[n] to be the element-wise xor operation between two boolean arrays. If w[n] is a Boolean array, the next generation can be expressed by

r(w[n]) = S_{-1}(w[n]) ^ S_1(w[n])

The xor operator ^ is associative and commutative. Using this property, the next few generations of w[n] can be computed by

r^2(w[n]) = ( S_{-2}(w[n]) ^ S_0(w[n]) ) ^ ( S_0(w[n]) ^ S_2(w[n]) )
          = S_{-2}(w[n]) ^ S_2(w[n])

If we let s_j = S_{-j}(w[n]) ^ S_j(w[n]), there is a pattern

r(w[n]) = s_1
r^2(w[n]) = s_2
r^3(w[n]) = s_3 ^ s_1
r^4(w[n]) = s_4
...
r(s_m) = s_{m-1} ^ s_{m+1}

Moreover, s_n = 0 (the array of zeroes) since a full circular shift is the original array. How do I use this to derive a non-iterative expression of r^t(w[n])?

Edit: The pattern is

[1]
[2]
[1,3]
[4]
[3,5]
[2,6]
[1,3,5,7]
[8]

Solution

  • Let's represent your input as a column vector a_0 of size n of elements of Z/2Z.

    You can compute the next generation vector, a_1 by using a matrix multiplication:

    a_1 = M.a_0 = |0 1 0 0 ... 0 0 0|  |a_01|
                  |1 0 1 0 ... 0 0 0|  |a_02|
                  |0 1 0 1 ... 0 0 0|  |a_03|
                  ....
                  |0 0 0 0 ... 0 1 0|  |... |
                  |0 0 0 0 ... 1 0 1|  |... |
                  |0 0 0 0 ... 0 1 0|  |a_0n|
    

    Given this recurrence relation, you can compute the generation at time t using the formula:

    a_t = M^t . a_0
    

    And you can easily compute M^t in O(n^3.log(t)) using repeated squaring.