EDIT:
Here is a version of basically the same algorithm but without any loops:
def snake_matrix(n):
# Make sequences: [0, 0, 1, 0, 1, 2, 0, 1, 2, 3, ...]
i = np.arange(n)
c = np.cumsum(i)
reps = np.repeat(c, i + 1)
seqs = np.arange(len(reps)) - reps
# Make inverted sequences: [0, 1, 0, 2, 1, 0, 3, 2, 1, 0, ...]
i_rep = np.repeat(i, i + 1)
seqs_inv = i_rep - seqs
# Select sequences for row and column indices
seq_even_mask = (i_rep % 2 == 0)
# Row inverts even sequences
row = np.where(seq_even_mask, seqs, seqs_inv)
# Column inverts odd sequences
col = np.where(~seq_even_mask, seqs, seqs_inv)
# Mirror for lower right corner
row = np.concatenate([row, n - 1 - row[len(row) - n - 1::-1]])
col = np.concatenate([col, n - 1 - col[len(col) - n - 1::-1]])
m = np.empty((n, n), dtype=int)
m[row, col] = np.arange(n * n)
return m
Interestingly, after a couple of benchmarks it seems that depending on the size this may or may not be faster than the previous algorithm.
Here is another solution with NumPy. I don't know if there is any other way to make this better (without loops, or in this case list comprehensions), but at least it does not loop over every single element. This one only works for square matrices though.
import numpy as np
def snake_matrix(n):
# Sequences for indexing top left triangle: [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]...
seqs = [np.arange(i + 1) for i in range(n)]
# Row indices reverse odd sequences
row = np.concatenate([seq if i % 2 == 0 else seq[::-1] for i, seq in enumerate(seqs)])
# Column indices reverse even sequences
col = np.concatenate([seq if i % 2 == 1 else seq[::-1] for i, seq in enumerate(seqs)])
# Indices for bottom right triangle "mirror" top left triangle
row = np.concatenate([row, n - 1 - row[len(row) - n - 1::-1]])
col = np.concatenate([col, n - 1 - col[len(col) - n - 1::-1]])
# Make matrix
m = np.empty((n, n), dtype=int)
m[row, col] = np.arange(n * n)
return m
print(snake_matrix(6))
Output:
[[ 0 2 3 9 10 20]
[ 1 4 8 11 19 21]
[ 5 7 12 18 22 29]
[ 6 13 17 23 28 30]
[14 16 24 27 31 34]
[15 25 26 32 33 35]]
There is some more information about this kind of enumeration in OEIS A319571 sequence (although that refers to the general sequence for an infinite grid, in this case you would have one enumeration starting at the top left and another at the bottom right).