AllenDowney/ThinkComplexity2

Chapter 6 notebook: Straightforward translation of GoL rules using loops doesn't handle boundaries correctly

gwtaylor opened this issue · 1 comments

Note that the output for the straightforward loop-based implementation is different from the correlation-based versions. In the static version of the notebook, the input is:

[[1 1 1 0 1 1 1 1 0 1]
 [0 1 0 1 0 1 0 0 1 1]
 [0 0 1 0 0 1 1 0 0 1]
 [0 0 0 0 0 1 1 0 0 1]
 [0 0 0 1 0 1 1 1 1 1]
 [0 0 1 0 1 0 1 0 0 0]
 [0 0 0 1 0 0 0 0 0 0]
 [0 1 0 0 0 1 0 1 1 0]
 [1 1 0 1 0 0 1 1 1 1]
 [0 0 1 0 0 0 0 0 0 0]]

The output for the loop-based implementation is:

[[0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0]
 [0 0 1 0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 1 0]
 [0 0 1 0 1 0 1 0 1 0]
 [0 0 1 1 1 1 1 1 0 0]
 [0 1 0 0 1 0 0 0 0 0]
 [0 1 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]

The output for the cross correlation version is:

[[1 1 1 1 1 1 1 1 0 1]
 [1 0 0 1 0 0 0 0 0 1]
 [0 0 1 0 0 0 0 1 0 1]
 [0 0 0 0 0 0 0 0 0 1]
 [0 0 0 1 0 0 0 0 1 1]
 [0 0 1 0 1 0 1 0 1 0]
 [0 0 1 1 1 1 1 1 0 0]
 [1 1 0 0 1 0 0 0 0 1]
 [1 1 0 0 0 0 1 0 0 1]
 [0 1 1 0 0 0 0 1 1 0]]

Note that the difference is on the boundaries, and that's because the following line doesn't work for the boundaries:

neighbors = a[i-1:i+2, j-1:j+2]

A student in my class made this observation, and also supplied an alternative version that explicitly checks for boundaries. I have confirmed that this version produces the same output as the cross correlation version.

b = np.zeros_like(a)
rows, cols = a.shape
for i in range(0, rows):
    for j in range(0, cols):
        state = a[i, j]
        
        if i == 0 and j == 0:
            neighbors = a[i:i+2, j:j+2]
        elif j == 0:
            neighbors = a[i-1:i+2, j:j+2]
        elif i == 0:
            neighbors = a[i:i+2, j-1:j+2]
        else:
            neighbors = a[i-1:i+2, j-1:j+2]
            
        k = np.sum(neighbors) - state
        if state:
            if k==2 or k==3:
                b[i, j] = 1
        else:
            if k == 3:
                b[i, j] = 1

print(b)

This is a good catch, and thanks to your student for the updated code.
I am inclined to leave this issue along for now, but I will leave it open in case other readers are bothered.