How to generate a 2d array forming a chequerboard without numpy?
Question:
The problem is the following:
chequerboard(n, m, offset_h, offset_w)
, all arguments >=1, returns an n
by m
chequerboard where each (region) is of size offset_h by offset_w, the top left cell is always filled, and only the right hand and bottom edge may contain incomplete chequers.
Examples:
chequerboard(6, 8, 2, 2) returns
[['#', '#', ' ', ' ', '#', '#', ' ', ' '],
['#', '#', ' ', ' ', '#', '#', ' ', ' '],
[' ', ' ', '#', '#', ' ', ' ', '#', '#'],
[' ', ' ', '#', '#', ' ', ' ', '#', '#'],
['#', '#', ' ', ' ', '#', '#', ' ', ' '],
['#', '#', ' ', ' ', '#', '#', ' ', ' ']]
chequerboard(5, 7, 2, 3) returns
[['#', '#', '#', ' ', ' ', ' ', '#'],
['#', '#', '#', ' ', ' ', ' ', '#'],
[' ', ' ', ' ', '#', '#', '#', ' '],
[' ', ' ', ' ', '#', '#', '#', ' '],
['#', '#', '#', ' ', ' ', ' ', '#']]
How can I solve it without Numpy? i.e., using only vanilla python?
Answers:
You could utilize the modulo operator (%
):
def print_chequerboard(board: list[list[str]]) -> None:
for row in board:
print(row)
def chequerboard(n: int, m: int, offset_h: int, offset_w: int) -> list[list[str]]:
board = [[' ' for _ in range(m)] for _ in range(n)]
for i in range(n):
for j in range(m):
if i // offset_h % 2 == 0 and j // offset_w % 2 == 0 or
i // offset_h % 2 == 1 and j // offset_w % 2 == 1:
board[i][j] = '#'
return board
def main() -> None:
print('chequerboard(6, 8, 2, 2): ')
print_chequerboard(chequerboard(6, 8, 2, 2))
print('chequerboard(5, 7, 2, 3): ')
print_chequerboard(chequerboard(5, 7, 2, 3))
print('chequerboard(6, 8, 2, 2): ')
print_chequerboard(chequerboard(6, 8, 2, 2))
print('chequerboard(5, 7, 2, 3): ')
print_chequerboard(chequerboard(5, 7, 2, 3))
if __name__ == '__main__':
main()
Output:
chequerboard(6, 8, 2, 2):
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
chequerboard(5, 7, 2, 3):
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
chequerboard(6, 8, 2, 2):
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
chequerboard(5, 7, 2, 3):
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
You can do it by creating the first row and then repeating it offset_h
times. This is then rolled by offset_w
for the subsequent offset_h
rows. Each row is sliced to the desired size and the process is repeated.
def ceiling(a, b):
return -(a//-b)
def chequerboard(n, m, offset_h, offset_w):
pattern = ['#']*offset_w+[' ']*offset_w
row = pattern + pattern * (ceiling(m, 2*offset_w)-1)
return [(row[offset_w:]+row[:offset_w])[:m] if i % 2 else row[:m] for i in range(ceiling(n, offset_h)) for _ in range(offset_h)][:n]
Test:
for i in ((6, 8, 2, 2), (5, 7, 2, 3)):
for j in chequerboard(*i):
print(j)
print('n')
Output:
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
Benchmarks:
assert chequerboard(6, 8, 2, 2) == chequerboard_sash(6, 8, 2, 2)
%timeit chequerboard(6, 8, 2, 2)
%timeit chequerboard_sash(6, 8, 2, 2)
Output:
2.1 µs ± 30.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
10.3 µs ± 74.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
The problem is the following:
chequerboard(n, m, offset_h, offset_w)
, all arguments >=1, returns an n
by m
chequerboard where each (region) is of size offset_h by offset_w, the top left cell is always filled, and only the right hand and bottom edge may contain incomplete chequers.
Examples:
chequerboard(6, 8, 2, 2) returns
[['#', '#', ' ', ' ', '#', '#', ' ', ' '],
['#', '#', ' ', ' ', '#', '#', ' ', ' '],
[' ', ' ', '#', '#', ' ', ' ', '#', '#'],
[' ', ' ', '#', '#', ' ', ' ', '#', '#'],
['#', '#', ' ', ' ', '#', '#', ' ', ' '],
['#', '#', ' ', ' ', '#', '#', ' ', ' ']]
chequerboard(5, 7, 2, 3) returns
[['#', '#', '#', ' ', ' ', ' ', '#'],
['#', '#', '#', ' ', ' ', ' ', '#'],
[' ', ' ', ' ', '#', '#', '#', ' '],
[' ', ' ', ' ', '#', '#', '#', ' '],
['#', '#', '#', ' ', ' ', ' ', '#']]
How can I solve it without Numpy? i.e., using only vanilla python?
You could utilize the modulo operator (%
):
def print_chequerboard(board: list[list[str]]) -> None:
for row in board:
print(row)
def chequerboard(n: int, m: int, offset_h: int, offset_w: int) -> list[list[str]]:
board = [[' ' for _ in range(m)] for _ in range(n)]
for i in range(n):
for j in range(m):
if i // offset_h % 2 == 0 and j // offset_w % 2 == 0 or
i // offset_h % 2 == 1 and j // offset_w % 2 == 1:
board[i][j] = '#'
return board
def main() -> None:
print('chequerboard(6, 8, 2, 2): ')
print_chequerboard(chequerboard(6, 8, 2, 2))
print('chequerboard(5, 7, 2, 3): ')
print_chequerboard(chequerboard(5, 7, 2, 3))
print('chequerboard(6, 8, 2, 2): ')
print_chequerboard(chequerboard(6, 8, 2, 2))
print('chequerboard(5, 7, 2, 3): ')
print_chequerboard(chequerboard(5, 7, 2, 3))
if __name__ == '__main__':
main()
Output:
chequerboard(6, 8, 2, 2):
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
chequerboard(5, 7, 2, 3):
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
chequerboard(6, 8, 2, 2):
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
chequerboard(5, 7, 2, 3):
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
You can do it by creating the first row and then repeating it offset_h
times. This is then rolled by offset_w
for the subsequent offset_h
rows. Each row is sliced to the desired size and the process is repeated.
def ceiling(a, b):
return -(a//-b)
def chequerboard(n, m, offset_h, offset_w):
pattern = ['#']*offset_w+[' ']*offset_w
row = pattern + pattern * (ceiling(m, 2*offset_w)-1)
return [(row[offset_w:]+row[:offset_w])[:m] if i % 2 else row[:m] for i in range(ceiling(n, offset_h)) for _ in range(offset_h)][:n]
Test:
for i in ((6, 8, 2, 2), (5, 7, 2, 3)):
for j in chequerboard(*i):
print(j)
print('n')
Output:
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
[' ', ' ', '#', '#', ' ', ' ', '#', '#']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', ' ', ' ', '#', '#', ' ', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
['#', '#', '#', ' ', ' ', ' ', '#']
[' ', ' ', ' ', '#', '#', '#', ' ']
[' ', ' ', ' ', '#', '#', '#', ' ']
['#', '#', '#', ' ', ' ', ' ', '#']
Benchmarks:
assert chequerboard(6, 8, 2, 2) == chequerboard_sash(6, 8, 2, 2)
%timeit chequerboard(6, 8, 2, 2)
%timeit chequerboard_sash(6, 8, 2, 2)
Output:
2.1 µs ± 30.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
10.3 µs ± 74.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)